New drop of the jmonkeyengine library A new drop of the jmonkeyengine library sources, based on a 2012-03-05 snapshot. Fixes a few unnecessary memory allocations in the main rendering loop. Change-Id: I51ac0942fe87204df102cfdce746b59a5cb5ff85
diff --git a/engine/src/android/com/jme3/app/AndroidHarness.java b/engine/src/android/com/jme3/app/AndroidHarness.java index 6631003..0ad2ba1 100644 --- a/engine/src/android/com/jme3/app/AndroidHarness.java +++ b/engine/src/android/com/jme3/app/AndroidHarness.java
@@ -70,6 +70,10 @@ */ protected boolean mouseEventsInvertY = true; /** + * if true finish this activity when the jme app is stopped + */ + protected boolean finishOnAppStop = true; + /** * Title of the exit dialog, default is "Do you want to exit?" */ protected String exitDialogTitle = "Do you want to exit?"; @@ -131,7 +135,7 @@ boolean bIsLogFormatSet = false; do { if (log.getHandlers().length == 0) { - log = logger.getParent(); + log = log.getParent(); if (log != null) { for (Handler h : log.getHandlers()) { //h.setFormatter(new SimpleFormatter()); @@ -395,4 +399,10 @@ } } } + + public boolean isFinishOnAppStop() { + return finishOnAppStop; + } + + } diff --git a/engine/src/android/com/jme3/asset/AndroidAssetManager.java b/engine/src/android/com/jme3/asset/AndroidAssetManager.java index c6f0b17..b2140dd 100644 --- a/engine/src/android/com/jme3/asset/AndroidAssetManager.java +++ b/engine/src/android/com/jme3/asset/AndroidAssetManager.java
@@ -37,6 +37,7 @@ import com.jme3.texture.Texture; import com.jme3.texture.plugins.AndroidImageLoader; import java.net.URL; +import java.util.logging.Level; import java.util.logging.Logger; /** @@ -57,6 +58,14 @@ //this(Thread.currentThread().getContextClassLoader().getResource("com/jme3/asset/Android.cfg")); this(null); } + + private void registerLoaderSafe(Class<? extends AssetLoader> loaderClass, String ... extensions) { + try { + registerLoader(loaderClass, extensions); + } catch (Exception e){ + logger.log(Level.WARNING, "Failed to load AssetLoader", e); + } + } /** * AndroidAssetManager constructor @@ -67,25 +76,29 @@ System.setProperty("org.xml.sax.driver", "org.xmlpull.v1.sax2.Driver"); // Set Default Android config - this.registerLocator("", AndroidLocator.class); - this.registerLocator("", ClasspathLocator.class); - this.registerLoader(AndroidImageLoader.class, "jpg", "bmp", "gif", "png", "jpeg"); - this.registerLoader(AndroidAudioLoader.class, "ogg", "mp3", "wav"); - this.registerLoader(com.jme3.material.plugins.J3MLoader.class, "j3m"); - this.registerLoader(com.jme3.material.plugins.J3MLoader.class, "j3md"); - this.registerLoader(com.jme3.font.plugins.BitmapFontLoader.class, "fnt"); - this.registerLoader(com.jme3.texture.plugins.DDSLoader.class, "dds"); - this.registerLoader(com.jme3.texture.plugins.PFMLoader.class, "pfm"); - this.registerLoader(com.jme3.texture.plugins.HDRLoader.class, "hdr"); - this.registerLoader(com.jme3.texture.plugins.TGALoader.class, "tga"); - this.registerLoader(com.jme3.export.binary.BinaryImporter.class, "j3o"); - this.registerLoader(com.jme3.scene.plugins.OBJLoader.class, "obj"); - this.registerLoader(com.jme3.scene.plugins.MTLLoader.class, "mtl"); - this.registerLoader(com.jme3.scene.plugins.ogre.MeshLoader.class, "meshxml", "mesh.xml"); - this.registerLoader(com.jme3.scene.plugins.ogre.SkeletonLoader.class, "skeletonxml", "skeleton.xml"); - this.registerLoader(com.jme3.scene.plugins.ogre.MaterialLoader.class, "material"); - this.registerLoader(com.jme3.scene.plugins.ogre.SceneLoader.class, "scene"); - this.registerLoader(com.jme3.shader.plugins.GLSLLoader.class, "vert", "frag", "glsl", "glsllib"); + registerLocator("", AndroidLocator.class); + registerLocator("", ClasspathLocator.class); + + registerLoader(AndroidImageLoader.class, "jpg", "bmp", "gif", "png", "jpeg"); + registerLoader(AndroidAudioLoader.class, "ogg", "mp3", "wav"); + registerLoader(com.jme3.material.plugins.J3MLoader.class, "j3m"); + registerLoader(com.jme3.material.plugins.J3MLoader.class, "j3md"); + registerLoader(com.jme3.shader.plugins.GLSLLoader.class, "vert", "frag", "glsl", "glsllib"); + registerLoader(com.jme3.export.binary.BinaryImporter.class, "j3o"); + registerLoader(com.jme3.font.plugins.BitmapFontLoader.class, "fnt"); + + // Less common loaders (especially on Android) + registerLoaderSafe(com.jme3.texture.plugins.DDSLoader.class, "dds"); + registerLoaderSafe(com.jme3.texture.plugins.PFMLoader.class, "pfm"); + registerLoaderSafe(com.jme3.texture.plugins.HDRLoader.class, "hdr"); + registerLoaderSafe(com.jme3.texture.plugins.TGALoader.class, "tga"); + registerLoaderSafe(com.jme3.scene.plugins.OBJLoader.class, "obj"); + registerLoaderSafe(com.jme3.scene.plugins.MTLLoader.class, "mtl"); + registerLoaderSafe(com.jme3.scene.plugins.ogre.MeshLoader.class, "mesh.xml"); + registerLoaderSafe(com.jme3.scene.plugins.ogre.SkeletonLoader.class, "skeleton.xml"); + registerLoaderSafe(com.jme3.scene.plugins.ogre.MaterialLoader.class, "material"); + registerLoaderSafe(com.jme3.scene.plugins.ogre.SceneLoader.class, "scene"); + logger.info("AndroidAssetManager created."); } diff --git a/engine/src/android/com/jme3/asset/plugins/AndroidLocator.java b/engine/src/android/com/jme3/asset/plugins/AndroidLocator.java index 01b1cab..225faf3 100644 --- a/engine/src/android/com/jme3/asset/plugins/AndroidLocator.java +++ b/engine/src/android/com/jme3/asset/plugins/AndroidLocator.java
@@ -4,9 +4,6 @@ import com.jme3.system.android.JmeAndroidSystem; import java.io.IOException; import java.io.InputStream; -import java.net.URL; -import java.net.URLConnection; -import java.util.logging.Level; import java.util.logging.Logger; public class AndroidLocator implements AssetLocator { @@ -80,7 +77,7 @@ assetPath = assetPath.replace("//", "/"); try { return create(manager, key, assetPath); - }catch (IOException ex){ + } catch (IOException ex) { // This is different handling than URL locator // since classpath locating would return null at the getResource() // call, otherwise there's a more critical error... diff --git a/engine/src/android/com/jme3/audio/plugins/AndroidAudioLoader.java b/engine/src/android/com/jme3/audio/plugins/AndroidAudioLoader.java index fc9c03e..a11425b 100644 --- a/engine/src/android/com/jme3/audio/plugins/AndroidAudioLoader.java +++ b/engine/src/android/com/jme3/audio/plugins/AndroidAudioLoader.java
@@ -5,15 +5,16 @@ import com.jme3.audio.android.AndroidAudioData; import java.io.IOException; -public class AndroidAudioLoader implements AssetLoader -{ +/** + * <code>AndroidAudioLoader</code> will create an + * {@link AndroidAudioData} object with the specified asset key. + */ +public class AndroidAudioLoader implements AssetLoader { @Override - public Object load(AssetInfo assetInfo) throws IOException - { + public Object load(AssetInfo assetInfo) throws IOException { AndroidAudioData result = new AndroidAudioData(); - result.setAssetKey( assetInfo.getKey() ); + result.setAssetKey(assetInfo.getKey()); return result; } - } diff --git a/engine/src/android/com/jme3/input/android/AndroidInput.java b/engine/src/android/com/jme3/input/android/AndroidInput.java index ca1141d..9bbbe4e 100644 --- a/engine/src/android/com/jme3/input/android/AndroidInput.java +++ b/engine/src/android/com/jme3/input/android/AndroidInput.java
@@ -1,619 +1,620 @@ -package com.jme3.input.android; - -import android.content.Context; -import android.opengl.GLSurfaceView; -import android.util.AttributeSet; -import android.view.GestureDetector; -import android.view.KeyEvent; -import android.view.MotionEvent; -import android.view.ScaleGestureDetector; -import com.jme3.input.KeyInput; -import com.jme3.input.RawInputListener; -import com.jme3.input.TouchInput; -import com.jme3.input.event.MouseButtonEvent; -import com.jme3.input.event.MouseMotionEvent; -import com.jme3.input.event.TouchEvent; -import com.jme3.input.event.TouchEvent.Type; -import com.jme3.math.Vector2f; -import com.jme3.util.RingBuffer; -import java.util.HashMap; -import java.util.logging.Logger; - -/** - * <code>AndroidInput</code> is one of the main components that connect jme with android. Is derived from GLSurfaceView and handles all Inputs - * @author larynx - * - */ -public class AndroidInput extends GLSurfaceView implements TouchInput, - GestureDetector.OnGestureListener, GestureDetector.OnDoubleTapListener, ScaleGestureDetector.OnScaleGestureListener { - - final private static int MAX_EVENTS = 1024; - // Custom settings - public boolean mouseEventsEnabled = true; - public boolean mouseEventsInvertX = false; - public boolean mouseEventsInvertY = false; - public boolean keyboardEventsEnabled = false; - public boolean dontSendHistory = false; - // Used to transfer events from android thread to GLThread - final private RingBuffer<TouchEvent> eventQueue = new RingBuffer<TouchEvent>(MAX_EVENTS); - final private RingBuffer<TouchEvent> eventPoolUnConsumed = new RingBuffer<TouchEvent>(MAX_EVENTS); - final private RingBuffer<TouchEvent> eventPool = new RingBuffer<TouchEvent>(MAX_EVENTS); - final private HashMap<Integer, Vector2f> lastPositions = new HashMap<Integer, Vector2f>(); - // Internal - private ScaleGestureDetector scaledetector; - private GestureDetector detector; - private int lastX; - private int lastY; - private final static Logger logger = Logger.getLogger(AndroidInput.class.getName()); - private boolean isInitialized = false; - private RawInputListener listener = null; - private static final int[] ANDROID_TO_JME = { - 0x0, // unknown - 0x0, // key code soft left - 0x0, // key code soft right - KeyInput.KEY_HOME, - KeyInput.KEY_ESCAPE, // key back - 0x0, // key call - 0x0, // key endcall - KeyInput.KEY_0, - KeyInput.KEY_1, - KeyInput.KEY_2, - KeyInput.KEY_3, - KeyInput.KEY_4, - KeyInput.KEY_5, - KeyInput.KEY_6, - KeyInput.KEY_7, - KeyInput.KEY_8, - KeyInput.KEY_9, - KeyInput.KEY_MULTIPLY, - 0x0, // key pound - KeyInput.KEY_UP, - KeyInput.KEY_DOWN, - KeyInput.KEY_LEFT, - KeyInput.KEY_RIGHT, - KeyInput.KEY_RETURN, // dpad center - 0x0, // volume up - 0x0, // volume down - KeyInput.KEY_POWER, // power (?) - 0x0, // camera - 0x0, // clear - KeyInput.KEY_A, - KeyInput.KEY_B, - KeyInput.KEY_C, - KeyInput.KEY_D, - KeyInput.KEY_E, - KeyInput.KEY_F, - KeyInput.KEY_G, - KeyInput.KEY_H, - KeyInput.KEY_I, - KeyInput.KEY_J, - KeyInput.KEY_K, - KeyInput.KEY_L, - KeyInput.KEY_M, - KeyInput.KEY_N, - KeyInput.KEY_O, - KeyInput.KEY_P, - KeyInput.KEY_Q, - KeyInput.KEY_R, - KeyInput.KEY_S, - KeyInput.KEY_T, - KeyInput.KEY_U, - KeyInput.KEY_V, - KeyInput.KEY_W, - KeyInput.KEY_X, - KeyInput.KEY_Y, - KeyInput.KEY_Z, - KeyInput.KEY_COMMA, - KeyInput.KEY_PERIOD, - KeyInput.KEY_LMENU, - KeyInput.KEY_RMENU, - KeyInput.KEY_LSHIFT, - KeyInput.KEY_RSHIFT, - // 0x0, // fn - // 0x0, // cap (?) - - KeyInput.KEY_TAB, - KeyInput.KEY_SPACE, - 0x0, // sym (?) symbol - 0x0, // explorer - 0x0, // envelope - KeyInput.KEY_RETURN, // newline/enter - KeyInput.KEY_DELETE, - KeyInput.KEY_GRAVE, - KeyInput.KEY_MINUS, - KeyInput.KEY_EQUALS, - KeyInput.KEY_LBRACKET, - KeyInput.KEY_RBRACKET, - KeyInput.KEY_BACKSLASH, - KeyInput.KEY_SEMICOLON, - KeyInput.KEY_APOSTROPHE, - KeyInput.KEY_SLASH, - KeyInput.KEY_AT, // at (@) - KeyInput.KEY_NUMLOCK, //0x0, // num - 0x0, //headset hook - 0x0, //focus - KeyInput.KEY_ADD, - KeyInput.KEY_LMETA, //menu - 0x0,//notification - 0x0,//search - 0x0,//media play/pause - 0x0,//media stop - 0x0,//media next - 0x0,//media previous - 0x0,//media rewind - 0x0,//media fastforward - 0x0,//mute - }; - - public AndroidInput(Context ctx, AttributeSet attribs) { - super(ctx, attribs); - detector = new GestureDetector(null, this, null, false); - scaledetector = new ScaleGestureDetector(ctx, this); - - } - - public AndroidInput(Context ctx) { - super(ctx); - detector = new GestureDetector(null, this, null, false); - scaledetector = new ScaleGestureDetector(ctx, this); - } - - private TouchEvent getNextFreeTouchEvent() { - return getNextFreeTouchEvent(false); - } - - /** - * Fetches a touch event from the reuse pool - * @param wait if true waits for a reusable event to get available/released by an other thread, if false returns a new one if needed - * @return a usable TouchEvent - */ - private TouchEvent getNextFreeTouchEvent(boolean wait) { - TouchEvent evt = null; - synchronized (eventPoolUnConsumed) { - int size = eventPoolUnConsumed.size(); - while (size > 0) { - evt = eventPoolUnConsumed.pop(); - if (!evt.isConsumed()) { - eventPoolUnConsumed.push(evt); - evt = null; - } else { - break; - } - size--; - } - } - - - if (evt == null) { - if (eventPool.isEmpty() && wait) { - logger.warning("eventPool buffer underrun"); - boolean isEmpty; - do { - synchronized (eventPool) { - isEmpty = eventPool.isEmpty(); - } - try { - Thread.sleep(50); - } catch (InterruptedException e) { - } - } while (isEmpty); - synchronized (eventPool) { - evt = eventPool.pop(); - } - } else if (eventPool.isEmpty()) { - evt = new TouchEvent(); - logger.warning("eventPool buffer underrun"); - } else { - synchronized (eventPool) { - evt = eventPool.pop(); - } - } - } - return evt; - } - - /** - * onTouchEvent gets called from android thread on touchpad events - */ - @Override - public boolean onTouchEvent(MotionEvent event) { - boolean bWasHandled = false; - TouchEvent touch; - // System.out.println("native : " + event.getAction()); - int action = event.getAction() & MotionEvent.ACTION_MASK; - int pointerIndex = (event.getAction() & MotionEvent.ACTION_POINTER_INDEX_MASK) - >> MotionEvent.ACTION_POINTER_INDEX_SHIFT; - int pointerId = event.getPointerId(pointerIndex); - - // final int historySize = event.getHistorySize(); - //final int pointerCount = event.getPointerCount(); - - - switch (action) { - - case MotionEvent.ACTION_POINTER_DOWN: - case MotionEvent.ACTION_DOWN: - - - touch = getNextFreeTouchEvent(); - touch.set(Type.DOWN, event.getX(pointerIndex), this.getHeight() - event.getY(pointerIndex), 0, 0); - touch.setPointerId(pointerId); - touch.setTime(event.getEventTime()); - touch.setPressure(event.getPressure(pointerIndex)); - processEvent(touch); - - bWasHandled = true; - break; - - case MotionEvent.ACTION_POINTER_UP: - case MotionEvent.ACTION_CANCEL: - case MotionEvent.ACTION_UP: - - touch = getNextFreeTouchEvent(); - touch.set(Type.UP, event.getX(pointerIndex), this.getHeight() - event.getY(pointerIndex), 0, 0); - touch.setPointerId(pointerId); - touch.setTime(event.getEventTime()); - touch.setPressure(event.getPressure(pointerIndex)); - processEvent(touch); - - - bWasHandled = true; - break; - case MotionEvent.ACTION_MOVE: - - - // Convert all pointers into events - for (int p = 0; p < event.getPointerCount(); p++) { - Vector2f lastPos = lastPositions.get(pointerIndex); - if (lastPos == null) { - lastPos = new Vector2f(event.getX(pointerIndex), this.getHeight() - event.getY(pointerIndex)); - lastPositions.put(pointerId, lastPos); - } - touch = getNextFreeTouchEvent(); - touch.set(Type.MOVE, event.getX(pointerIndex), this.getHeight() - event.getY(pointerIndex), event.getX(pointerIndex) - lastPos.x, this.getHeight() - event.getY(pointerIndex) - lastPos.y); - touch.setPointerId(pointerId); - touch.setTime(event.getEventTime()); - touch.setPressure(event.getPressure(pointerIndex)); - processEvent(touch); - lastPos.set(event.getX(pointerIndex), this.getHeight() - event.getY(pointerIndex)); - } - bWasHandled = true; - break; - - - case MotionEvent.ACTION_OUTSIDE: - break; - - } - - // Try to detect gestures - this.detector.onTouchEvent(event); - this.scaledetector.onTouchEvent(event); - - return bWasHandled; - } - - @Override - public boolean onKeyDown(int keyCode, KeyEvent event) { - TouchEvent evt; - evt = getNextFreeTouchEvent(); - evt.set(TouchEvent.Type.KEY_DOWN); - evt.setKeyCode(keyCode); - evt.setCharacters(event.getCharacters()); - evt.setTime(event.getEventTime()); - - // Send the event - processEvent(evt); - - // Handle all keys ourself except Volume Up/Down - if ((keyCode == KeyEvent.KEYCODE_VOLUME_UP) || (keyCode == KeyEvent.KEYCODE_VOLUME_DOWN)) { - return false; - } else { - return true; - } - - } - - @Override - public boolean onKeyUp(int keyCode, KeyEvent event) { - TouchEvent evt; - evt = getNextFreeTouchEvent(); - evt.set(TouchEvent.Type.KEY_UP); - evt.setKeyCode(keyCode); - evt.setCharacters(event.getCharacters()); - evt.setTime(event.getEventTime()); - - // Send the event - processEvent(evt); - - // Handle all keys ourself except Volume Up/Down - if ((keyCode == KeyEvent.KEYCODE_VOLUME_UP) || (keyCode == KeyEvent.KEYCODE_VOLUME_DOWN)) { - return false; - } else { - return true; - } - } - - // ----------------------------------------- - // JME3 Input interface - @Override - public void initialize() { - TouchEvent item; - for (int i = 0; i < MAX_EVENTS; i++) { - item = new TouchEvent(); - eventPool.push(item); - } - isInitialized = true; - } - - @Override - public void destroy() { - isInitialized = false; - - // Clean up queues - while (!eventPool.isEmpty()) { - eventPool.pop(); - } - while (!eventQueue.isEmpty()) { - eventQueue.pop(); - } - } - - @Override - public boolean isInitialized() { - return isInitialized; - } - - @Override - public void setInputListener(RawInputListener listener) { - this.listener = listener; - } - - @Override - public long getInputTimeNanos() { - return System.nanoTime(); - } - // ----------------------------------------- - - private void processEvent(TouchEvent event) { - synchronized (eventQueue) { - eventQueue.push(event); - } - } - - // --------------- INSIDE GLThread --------------- - @Override - public void update() { - generateEvents(); - } - - private void generateEvents() { - if (listener != null) { - TouchEvent event; - MouseButtonEvent btn; - int newX; - int newY; - - while (!eventQueue.isEmpty()) { - synchronized (eventQueue) { - event = eventQueue.pop(); - } - if (event != null) { - listener.onTouchEvent(event); - - if (mouseEventsEnabled) { - if (mouseEventsInvertX) { - newX = this.getWidth() - (int) event.getX(); - } else { - newX = (int) event.getX(); - } - - if (mouseEventsInvertY) { - newY = this.getHeight() - (int) event.getY(); - } else { - newY = (int) event.getY(); - } - - switch (event.getType()) { - case DOWN: - // Handle mouse down event - btn = new MouseButtonEvent(0, true, newX, newY); - btn.setTime(event.getTime()); - listener.onMouseButtonEvent(btn); - // Store current pos - lastX = -1; - lastY = -1; - break; - - case UP: - // Handle mouse up event - btn = new MouseButtonEvent(0, false, newX, newY); - btn.setTime(event.getTime()); - listener.onMouseButtonEvent(btn); - // Store current pos - lastX = -1; - lastY = -1; - break; - - case MOVE: - int dx; - int dy; - if (lastX != -1) { - dx = newX - lastX; - dy = newY - lastY; - } else { - dx = 0; - dy = 0; - } - MouseMotionEvent mot = new MouseMotionEvent(newX, newY, dx, dy, 0, 0); - mot.setTime(event.getTime()); - listener.onMouseMotionEvent(mot); - lastX = newX; - lastY = newY; - break; - } - - - } - } - - if (event.isConsumed() == false) { - synchronized (eventPoolUnConsumed) { - eventPoolUnConsumed.push(event); - } - - } else { - synchronized (eventPool) { - eventPool.push(event); - } - } - } - - } - } - // --------------- ENDOF INSIDE GLThread --------------- - - // --------------- Gesture detected callback events --------------- - public boolean onDown(MotionEvent event) { - return false; - } - - public void onLongPress(MotionEvent event) { - TouchEvent touch = getNextFreeTouchEvent(); - touch.set(Type.LONGPRESSED, event.getX(), this.getHeight() - event.getY(), 0f, 0f); - touch.setPointerId(0); - touch.setTime(event.getEventTime()); - processEvent(touch); - } - - public boolean onFling(MotionEvent event, MotionEvent event2, float vx, float vy) { - TouchEvent touch = getNextFreeTouchEvent(); - touch.set(Type.FLING, event.getX(), this.getHeight() - event.getY(), vx, vy); - touch.setPointerId(0); - touch.setTime(event.getEventTime()); - processEvent(touch); - - return true; - } - - public boolean onSingleTapConfirmed(MotionEvent event) { - //Nothing to do here the tap has already been detected. - return false; - } - - public boolean onDoubleTap(MotionEvent event) { - TouchEvent touch = getNextFreeTouchEvent(); - touch.set(Type.DOUBLETAP, event.getX(), this.getHeight() - event.getY(), 0f, 0f); - touch.setPointerId(0); - touch.setTime(event.getEventTime()); - processEvent(touch); - return true; - } - - public boolean onDoubleTapEvent(MotionEvent event) { - return false; - } - - public boolean onScaleBegin(ScaleGestureDetector scaleGestureDetector) { - TouchEvent touch = getNextFreeTouchEvent(); - touch.set(Type.SCALE_START, scaleGestureDetector.getFocusX(), scaleGestureDetector.getFocusY(), 0f, 0f); - touch.setPointerId(0); - touch.setTime(scaleGestureDetector.getEventTime()); - touch.setScaleSpan(scaleGestureDetector.getCurrentSpan()); - touch.setScaleFactor(scaleGestureDetector.getScaleFactor()); - processEvent(touch); - // System.out.println("scaleBegin"); - - return true; - } - - public boolean onScale(ScaleGestureDetector scaleGestureDetector) { - TouchEvent touch = getNextFreeTouchEvent(); - touch.set(Type.SCALE_MOVE, scaleGestureDetector.getFocusX(), this.getHeight() - scaleGestureDetector.getFocusY(), 0f, 0f); - touch.setPointerId(0); - touch.setTime(scaleGestureDetector.getEventTime()); - touch.setScaleSpan(scaleGestureDetector.getCurrentSpan()); - touch.setScaleFactor(scaleGestureDetector.getScaleFactor()); - processEvent(touch); - // System.out.println("scale"); - - return false; - } - - public void onScaleEnd(ScaleGestureDetector scaleGestureDetector) { - TouchEvent touch = getNextFreeTouchEvent(); - touch.set(Type.SCALE_END, scaleGestureDetector.getFocusX(), this.getHeight() - scaleGestureDetector.getFocusY(), 0f, 0f); - touch.setPointerId(0); - touch.setTime(scaleGestureDetector.getEventTime()); - touch.setScaleSpan(scaleGestureDetector.getCurrentSpan()); - touch.setScaleFactor(scaleGestureDetector.getScaleFactor()); - processEvent(touch); - } - - public boolean onScroll(MotionEvent e1, MotionEvent e2, float distanceX, float distanceY) { - TouchEvent touch = getNextFreeTouchEvent(); - touch.set(Type.SCROLL, e1.getX(), this.getHeight() - e1.getY(), distanceX, distanceY * (-1)); - touch.setPointerId(0); - touch.setTime(e1.getEventTime()); - processEvent(touch); - //System.out.println("scroll " + e1.getPointerCount()); - return false; - } - - public void onShowPress(MotionEvent event) { - TouchEvent touch = getNextFreeTouchEvent(); - touch.set(Type.SHOWPRESS, event.getX(), this.getHeight() - event.getY(), 0f, 0f); - touch.setPointerId(0); - touch.setTime(event.getEventTime()); - processEvent(touch); - } - - public boolean onSingleTapUp(MotionEvent event) { - TouchEvent touch = getNextFreeTouchEvent(); - touch.set(Type.TAP, event.getX(), this.getHeight() - event.getY(), 0f, 0f); - touch.setPointerId(0); - touch.setTime(event.getEventTime()); - processEvent(touch); - return true; - } - - @Override - public void setSimulateMouse(boolean simulate) { - mouseEventsEnabled = simulate; - } - - @Override - public void setSimulateKeyboard(boolean simulate) { - keyboardEventsEnabled = simulate; - } - - @Override - public void setOmitHistoricEvents(boolean dontSendHistory) { - this.dontSendHistory = dontSendHistory; - } - - // TODO: move to TouchInput - public boolean isMouseEventsEnabled() { - return mouseEventsEnabled; - } - - public void setMouseEventsEnabled(boolean mouseEventsEnabled) { - this.mouseEventsEnabled = mouseEventsEnabled; - } - - public boolean isMouseEventsInvertY() { - return mouseEventsInvertY; - } - - public void setMouseEventsInvertY(boolean mouseEventsInvertY) { - this.mouseEventsInvertY = mouseEventsInvertY; - } - - public boolean isMouseEventsInvertX() { - return mouseEventsInvertX; - } - - public void setMouseEventsInvertX(boolean mouseEventsInvertX) { - this.mouseEventsInvertX = mouseEventsInvertX; - } -} +package com.jme3.input.android; + +import android.content.Context; +import android.opengl.GLSurfaceView; +import android.util.AttributeSet; +import android.view.GestureDetector; +import android.view.KeyEvent; +import android.view.MotionEvent; +import android.view.ScaleGestureDetector; +import com.jme3.input.KeyInput; +import com.jme3.input.RawInputListener; +import com.jme3.input.TouchInput; +import com.jme3.input.event.MouseButtonEvent; +import com.jme3.input.event.MouseMotionEvent; +import com.jme3.input.event.TouchEvent; +import com.jme3.input.event.TouchEvent.Type; +import com.jme3.math.Vector2f; +import com.jme3.util.RingBuffer; +import java.util.HashMap; +import java.util.logging.Logger; + +/** + * <code>AndroidInput</code> is one of the main components that connect jme with android. Is derived from GLSurfaceView and handles all Inputs + * @author larynx + * + */ +public class AndroidInput extends GLSurfaceView implements + TouchInput, + GestureDetector.OnGestureListener, + GestureDetector.OnDoubleTapListener, + ScaleGestureDetector.OnScaleGestureListener { + + final private static int MAX_EVENTS = 1024; + // Custom settings + public boolean mouseEventsEnabled = true; + public boolean mouseEventsInvertX = false; + public boolean mouseEventsInvertY = false; + public boolean keyboardEventsEnabled = false; + public boolean dontSendHistory = false; + // Used to transfer events from android thread to GLThread + final private RingBuffer<TouchEvent> eventQueue = new RingBuffer<TouchEvent>(MAX_EVENTS); + final private RingBuffer<TouchEvent> eventPoolUnConsumed = new RingBuffer<TouchEvent>(MAX_EVENTS); + final private RingBuffer<TouchEvent> eventPool = new RingBuffer<TouchEvent>(MAX_EVENTS); + final private HashMap<Integer, Vector2f> lastPositions = new HashMap<Integer, Vector2f>(); + // Internal + private ScaleGestureDetector scaledetector; + private GestureDetector detector; + private int lastX; + private int lastY; + private final static Logger logger = Logger.getLogger(AndroidInput.class.getName()); + private boolean isInitialized = false; + private RawInputListener listener = null; + private static final int[] ANDROID_TO_JME = { + 0x0, // unknown + 0x0, // key code soft left + 0x0, // key code soft right + KeyInput.KEY_HOME, + KeyInput.KEY_ESCAPE, // key back + 0x0, // key call + 0x0, // key endcall + KeyInput.KEY_0, + KeyInput.KEY_1, + KeyInput.KEY_2, + KeyInput.KEY_3, + KeyInput.KEY_4, + KeyInput.KEY_5, + KeyInput.KEY_6, + KeyInput.KEY_7, + KeyInput.KEY_8, + KeyInput.KEY_9, + KeyInput.KEY_MULTIPLY, + 0x0, // key pound + KeyInput.KEY_UP, + KeyInput.KEY_DOWN, + KeyInput.KEY_LEFT, + KeyInput.KEY_RIGHT, + KeyInput.KEY_RETURN, // dpad center + 0x0, // volume up + 0x0, // volume down + KeyInput.KEY_POWER, // power (?) + 0x0, // camera + 0x0, // clear + KeyInput.KEY_A, + KeyInput.KEY_B, + KeyInput.KEY_C, + KeyInput.KEY_D, + KeyInput.KEY_E, + KeyInput.KEY_F, + KeyInput.KEY_G, + KeyInput.KEY_H, + KeyInput.KEY_I, + KeyInput.KEY_J, + KeyInput.KEY_K, + KeyInput.KEY_L, + KeyInput.KEY_M, + KeyInput.KEY_N, + KeyInput.KEY_O, + KeyInput.KEY_P, + KeyInput.KEY_Q, + KeyInput.KEY_R, + KeyInput.KEY_S, + KeyInput.KEY_T, + KeyInput.KEY_U, + KeyInput.KEY_V, + KeyInput.KEY_W, + KeyInput.KEY_X, + KeyInput.KEY_Y, + KeyInput.KEY_Z, + KeyInput.KEY_COMMA, + KeyInput.KEY_PERIOD, + KeyInput.KEY_LMENU, + KeyInput.KEY_RMENU, + KeyInput.KEY_LSHIFT, + KeyInput.KEY_RSHIFT, + // 0x0, // fn + // 0x0, // cap (?) + + KeyInput.KEY_TAB, + KeyInput.KEY_SPACE, + 0x0, // sym (?) symbol + 0x0, // explorer + 0x0, // envelope + KeyInput.KEY_RETURN, // newline/enter + KeyInput.KEY_DELETE, + KeyInput.KEY_GRAVE, + KeyInput.KEY_MINUS, + KeyInput.KEY_EQUALS, + KeyInput.KEY_LBRACKET, + KeyInput.KEY_RBRACKET, + KeyInput.KEY_BACKSLASH, + KeyInput.KEY_SEMICOLON, + KeyInput.KEY_APOSTROPHE, + KeyInput.KEY_SLASH, + KeyInput.KEY_AT, // at (@) + KeyInput.KEY_NUMLOCK, //0x0, // num + 0x0, //headset hook + 0x0, //focus + KeyInput.KEY_ADD, + KeyInput.KEY_LMETA, //menu + 0x0,//notification + 0x0,//search + 0x0,//media play/pause + 0x0,//media stop + 0x0,//media next + 0x0,//media previous + 0x0,//media rewind + 0x0,//media fastforward + 0x0,//mute + }; + + public AndroidInput(Context ctx, AttributeSet attribs) { + super(ctx, attribs); + detector = new GestureDetector(null, this, null, false); + scaledetector = new ScaleGestureDetector(ctx, this); + + } + + public AndroidInput(Context ctx) { + super(ctx); + detector = new GestureDetector(null, this, null, false); + scaledetector = new ScaleGestureDetector(ctx, this); + } + + private TouchEvent getNextFreeTouchEvent() { + return getNextFreeTouchEvent(false); + } + + /** + * Fetches a touch event from the reuse pool + * @param wait if true waits for a reusable event to get available/released + * by an other thread, if false returns a new one if needed. + * + * @return a usable TouchEvent + */ + private TouchEvent getNextFreeTouchEvent(boolean wait) { + TouchEvent evt = null; + synchronized (eventPoolUnConsumed) { + int size = eventPoolUnConsumed.size(); + while (size > 0) { + evt = eventPoolUnConsumed.pop(); + if (!evt.isConsumed()) { + eventPoolUnConsumed.push(evt); + evt = null; + } else { + break; + } + size--; + } + } + + if (evt == null) { + if (eventPool.isEmpty() && wait) { + logger.warning("eventPool buffer underrun"); + boolean isEmpty; + do { + synchronized (eventPool) { + isEmpty = eventPool.isEmpty(); + } + try { + Thread.sleep(50); + } catch (InterruptedException e) { + } + } while (isEmpty); + synchronized (eventPool) { + evt = eventPool.pop(); + } + } else if (eventPool.isEmpty()) { + evt = new TouchEvent(); + logger.warning("eventPool buffer underrun"); + } else { + synchronized (eventPool) { + evt = eventPool.pop(); + } + } + } + return evt; + } + + /** + * onTouchEvent gets called from android thread on touchpad events + */ + @Override + public boolean onTouchEvent(MotionEvent event) { + boolean bWasHandled = false; + TouchEvent touch; + // System.out.println("native : " + event.getAction()); + int action = event.getAction() & MotionEvent.ACTION_MASK; + int pointerIndex = (event.getAction() & MotionEvent.ACTION_POINTER_INDEX_MASK) + >> MotionEvent.ACTION_POINTER_INDEX_SHIFT; + int pointerId = event.getPointerId(pointerIndex); + + // final int historySize = event.getHistorySize(); + //final int pointerCount = event.getPointerCount(); + + switch (action) { + case MotionEvent.ACTION_POINTER_DOWN: + case MotionEvent.ACTION_DOWN: + touch = getNextFreeTouchEvent(); + touch.set(Type.DOWN, event.getX(pointerIndex), this.getHeight() - event.getY(pointerIndex), 0, 0); + touch.setPointerId(pointerId); + touch.setTime(event.getEventTime()); + touch.setPressure(event.getPressure(pointerIndex)); + processEvent(touch); + + bWasHandled = true; + break; + + case MotionEvent.ACTION_POINTER_UP: + case MotionEvent.ACTION_CANCEL: + case MotionEvent.ACTION_UP: + + touch = getNextFreeTouchEvent(); + touch.set(Type.UP, event.getX(pointerIndex), this.getHeight() - event.getY(pointerIndex), 0, 0); + touch.setPointerId(pointerId); + touch.setTime(event.getEventTime()); + touch.setPressure(event.getPressure(pointerIndex)); + processEvent(touch); + + + bWasHandled = true; + break; + case MotionEvent.ACTION_MOVE: + + + // Convert all pointers into events + for (int p = 0; p < event.getPointerCount(); p++) { + Vector2f lastPos = lastPositions.get(pointerIndex); + if (lastPos == null) { + lastPos = new Vector2f(event.getX(pointerIndex), this.getHeight() - event.getY(pointerIndex)); + lastPositions.put(pointerId, lastPos); + } + touch = getNextFreeTouchEvent(); + touch.set(Type.MOVE, event.getX(pointerIndex), this.getHeight() - event.getY(pointerIndex), event.getX(pointerIndex) - lastPos.x, this.getHeight() - event.getY(pointerIndex) - lastPos.y); + touch.setPointerId(pointerId); + touch.setTime(event.getEventTime()); + touch.setPressure(event.getPressure(pointerIndex)); + processEvent(touch); + lastPos.set(event.getX(pointerIndex), this.getHeight() - event.getY(pointerIndex)); + } + bWasHandled = true; + break; + case MotionEvent.ACTION_OUTSIDE: + break; + + } + + // Try to detect gestures + this.detector.onTouchEvent(event); + this.scaledetector.onTouchEvent(event); + + return bWasHandled; + } + + @Override + public boolean onKeyDown(int keyCode, KeyEvent event) { + TouchEvent evt; + evt = getNextFreeTouchEvent(); + evt.set(TouchEvent.Type.KEY_DOWN); + evt.setKeyCode(keyCode); + evt.setCharacters(event.getCharacters()); + evt.setTime(event.getEventTime()); + + // Send the event + processEvent(evt); + + // Handle all keys ourself except Volume Up/Down + if ((keyCode == KeyEvent.KEYCODE_VOLUME_UP) || (keyCode == KeyEvent.KEYCODE_VOLUME_DOWN)) { + return false; + } else { + return true; + } + } + + @Override + public boolean onKeyUp(int keyCode, KeyEvent event) { + TouchEvent evt; + evt = getNextFreeTouchEvent(); + evt.set(TouchEvent.Type.KEY_UP); + evt.setKeyCode(keyCode); + evt.setCharacters(event.getCharacters()); + evt.setTime(event.getEventTime()); + + // Send the event + processEvent(evt); + + // Handle all keys ourself except Volume Up/Down + if ((keyCode == KeyEvent.KEYCODE_VOLUME_UP) || (keyCode == KeyEvent.KEYCODE_VOLUME_DOWN)) { + return false; + } else { + return true; + } + } + + // ----------------------------------------- + // JME3 Input interface + @Override + public void initialize() { + TouchEvent item; + for (int i = 0; i < MAX_EVENTS; i++) { + item = new TouchEvent(); + eventPool.push(item); + } + isInitialized = true; + } + + @Override + public void destroy() { + isInitialized = false; + + // Clean up queues + while (!eventPool.isEmpty()) { + eventPool.pop(); + } + while (!eventQueue.isEmpty()) { + eventQueue.pop(); + } + } + + @Override + public boolean isInitialized() { + return isInitialized; + } + + @Override + public void setInputListener(RawInputListener listener) { + this.listener = listener; + } + + @Override + public long getInputTimeNanos() { + return System.nanoTime(); + } + // ----------------------------------------- + + private void processEvent(TouchEvent event) { + synchronized (eventQueue) { + eventQueue.push(event); + } + } + + // --------------- INSIDE GLThread --------------- + @Override + public void update() { + generateEvents(); + } + + private void generateEvents() { + if (listener != null) { + TouchEvent event; + MouseButtonEvent btn; + int newX; + int newY; + + while (!eventQueue.isEmpty()) { + synchronized (eventQueue) { + event = eventQueue.pop(); + } + if (event != null) { + listener.onTouchEvent(event); + + if (mouseEventsEnabled) { + if (mouseEventsInvertX) { + newX = this.getWidth() - (int) event.getX(); + } else { + newX = (int) event.getX(); + } + + if (mouseEventsInvertY) { + newY = this.getHeight() - (int) event.getY(); + } else { + newY = (int) event.getY(); + } + + switch (event.getType()) { + case DOWN: + // Handle mouse down event + btn = new MouseButtonEvent(0, true, newX, newY); + btn.setTime(event.getTime()); + listener.onMouseButtonEvent(btn); + // Store current pos + lastX = -1; + lastY = -1; + break; + + case UP: + // Handle mouse up event + btn = new MouseButtonEvent(0, false, newX, newY); + btn.setTime(event.getTime()); + listener.onMouseButtonEvent(btn); + // Store current pos + lastX = -1; + lastY = -1; + break; + + case MOVE: + int dx; + int dy; + if (lastX != -1) { + dx = newX - lastX; + dy = newY - lastY; + } else { + dx = 0; + dy = 0; + } + MouseMotionEvent mot = new MouseMotionEvent(newX, newY, dx, dy, 0, 0); + mot.setTime(event.getTime()); + listener.onMouseMotionEvent(mot); + lastX = newX; + lastY = newY; + break; + } + + + } + } + + if (event.isConsumed() == false) { + synchronized (eventPoolUnConsumed) { + eventPoolUnConsumed.push(event); + } + + } else { + synchronized (eventPool) { + eventPool.push(event); + } + } + } + + } + } + // --------------- ENDOF INSIDE GLThread --------------- + + // --------------- Gesture detected callback events --------------- + public boolean onDown(MotionEvent event) { + return false; + } + + public void onLongPress(MotionEvent event) { + TouchEvent touch = getNextFreeTouchEvent(); + touch.set(Type.LONGPRESSED, event.getX(), this.getHeight() - event.getY(), 0f, 0f); + touch.setPointerId(0); + touch.setTime(event.getEventTime()); + processEvent(touch); + } + + public boolean onFling(MotionEvent event, MotionEvent event2, float vx, float vy) { + TouchEvent touch = getNextFreeTouchEvent(); + touch.set(Type.FLING, event.getX(), this.getHeight() - event.getY(), vx, vy); + touch.setPointerId(0); + touch.setTime(event.getEventTime()); + processEvent(touch); + + return true; + } + + public boolean onSingleTapConfirmed(MotionEvent event) { + //Nothing to do here the tap has already been detected. + return false; + } + + public boolean onDoubleTap(MotionEvent event) { + TouchEvent touch = getNextFreeTouchEvent(); + touch.set(Type.DOUBLETAP, event.getX(), this.getHeight() - event.getY(), 0f, 0f); + touch.setPointerId(0); + touch.setTime(event.getEventTime()); + processEvent(touch); + return true; + } + + public boolean onDoubleTapEvent(MotionEvent event) { + return false; + } + + public boolean onScaleBegin(ScaleGestureDetector scaleGestureDetector) { + TouchEvent touch = getNextFreeTouchEvent(); + touch.set(Type.SCALE_START, scaleGestureDetector.getFocusX(), scaleGestureDetector.getFocusY(), 0f, 0f); + touch.setPointerId(0); + touch.setTime(scaleGestureDetector.getEventTime()); + touch.setScaleSpan(scaleGestureDetector.getCurrentSpan()); + touch.setScaleFactor(scaleGestureDetector.getScaleFactor()); + processEvent(touch); + // System.out.println("scaleBegin"); + + return true; + } + + public boolean onScale(ScaleGestureDetector scaleGestureDetector) { + TouchEvent touch = getNextFreeTouchEvent(); + touch.set(Type.SCALE_MOVE, scaleGestureDetector.getFocusX(), this.getHeight() - scaleGestureDetector.getFocusY(), 0f, 0f); + touch.setPointerId(0); + touch.setTime(scaleGestureDetector.getEventTime()); + touch.setScaleSpan(scaleGestureDetector.getCurrentSpan()); + touch.setScaleFactor(scaleGestureDetector.getScaleFactor()); + processEvent(touch); + // System.out.println("scale"); + + return false; + } + + public void onScaleEnd(ScaleGestureDetector scaleGestureDetector) { + TouchEvent touch = getNextFreeTouchEvent(); + touch.set(Type.SCALE_END, scaleGestureDetector.getFocusX(), this.getHeight() - scaleGestureDetector.getFocusY(), 0f, 0f); + touch.setPointerId(0); + touch.setTime(scaleGestureDetector.getEventTime()); + touch.setScaleSpan(scaleGestureDetector.getCurrentSpan()); + touch.setScaleFactor(scaleGestureDetector.getScaleFactor()); + processEvent(touch); + } + + public boolean onScroll(MotionEvent e1, MotionEvent e2, float distanceX, float distanceY) { + TouchEvent touch = getNextFreeTouchEvent(); + touch.set(Type.SCROLL, e1.getX(), this.getHeight() - e1.getY(), distanceX, distanceY * (-1)); + touch.setPointerId(0); + touch.setTime(e1.getEventTime()); + processEvent(touch); + //System.out.println("scroll " + e1.getPointerCount()); + return false; + } + + public void onShowPress(MotionEvent event) { + TouchEvent touch = getNextFreeTouchEvent(); + touch.set(Type.SHOWPRESS, event.getX(), this.getHeight() - event.getY(), 0f, 0f); + touch.setPointerId(0); + touch.setTime(event.getEventTime()); + processEvent(touch); + } + + public boolean onSingleTapUp(MotionEvent event) { + TouchEvent touch = getNextFreeTouchEvent(); + touch.set(Type.TAP, event.getX(), this.getHeight() - event.getY(), 0f, 0f); + touch.setPointerId(0); + touch.setTime(event.getEventTime()); + processEvent(touch); + return true; + } + + @Override + public void setSimulateMouse(boolean simulate) { + mouseEventsEnabled = simulate; + } + @Override + public boolean getSimulateMouse() { + return mouseEventsEnabled; + } + + @Override + public void setSimulateKeyboard(boolean simulate) { + keyboardEventsEnabled = simulate; + } + + @Override + public void setOmitHistoricEvents(boolean dontSendHistory) { + this.dontSendHistory = dontSendHistory; + } + + // TODO: move to TouchInput + public boolean isMouseEventsEnabled() { + return mouseEventsEnabled; + } + + public void setMouseEventsEnabled(boolean mouseEventsEnabled) { + this.mouseEventsEnabled = mouseEventsEnabled; + } + + public boolean isMouseEventsInvertY() { + return mouseEventsInvertY; + } + + public void setMouseEventsInvertY(boolean mouseEventsInvertY) { + this.mouseEventsInvertY = mouseEventsInvertY; + } + + public boolean isMouseEventsInvertX() { + return mouseEventsInvertX; + } + + public void setMouseEventsInvertX(boolean mouseEventsInvertX) { + this.mouseEventsInvertX = mouseEventsInvertX; + } +} diff --git a/engine/src/android/com/jme3/input/android/AndroidTouchInputListener.java b/engine/src/android/com/jme3/input/android/AndroidTouchInputListener.java index 34e4592..c8b2228 100644 --- a/engine/src/android/com/jme3/input/android/AndroidTouchInputListener.java +++ b/engine/src/android/com/jme3/input/android/AndroidTouchInputListener.java
@@ -6,14 +6,17 @@ import com.jme3.input.event.TouchEvent; /** - * AndroidTouchInputListener is an inputlistener interface which defines callbacks/events for android touch screens - * For use with class AndroidInput + * AndroidTouchInputListener is an inputlistener interface which defines + * callbacks/events for android touch screens For use with class AndroidInput + * * @author larynx * */ -public interface AndroidTouchInputListener extends RawInputListener -{ +public interface AndroidTouchInputListener extends RawInputListener { + public void onTouchEvent(TouchEvent evt); + public void onMotionEvent(MotionEvent evt); + public void onAndroidKeyEvent(KeyEvent evt); } diff --git a/engine/src/android/com/jme3/renderer/android/OGLESShaderRenderer.java b/engine/src/android/com/jme3/renderer/android/OGLESShaderRenderer.java index b5a8c14..7e7af29 100644 --- a/engine/src/android/com/jme3/renderer/android/OGLESShaderRenderer.java +++ b/engine/src/android/com/jme3/renderer/android/OGLESShaderRenderer.java
@@ -31,9 +31,7 @@ */ package com.jme3.renderer.android; -import android.graphics.Bitmap; import android.opengl.GLES10; -import android.opengl.GLES11; import android.opengl.GLES20; import android.os.Build; import com.jme3.asset.AndroidImageInfo; @@ -60,14 +58,11 @@ import com.jme3.util.BufferUtils; import com.jme3.util.ListMap; import com.jme3.util.NativeObjectManager; -import com.jme3.util.SafeArrayList; import java.nio.*; -import java.util.ArrayList; import java.util.EnumSet; import java.util.List; import java.util.logging.Level; import java.util.logging.Logger; -import javax.microedition.khronos.opengles.GL10; public class OGLESShaderRenderer implements Renderer { @@ -360,25 +355,10 @@ } applyRenderState(RenderState.DEFAULT); -// GLES20.glClearDepthf(1.0f); - - if (verboseLogging) { - logger.info("GLES20.glDisable(GL10.GL_DITHER)"); - } - - GLES20.glDisable(GL10.GL_DITHER); + GLES20.glDisable(GLES20.GL_DITHER); checkGLError(); - if (verboseLogging) { - logger.info("GLES20.glHint(GL10.GL_PERSPECTIVE_CORRECTION_HINT, GL10.GL_FASTEST)"); - } - - //It seems that GL10.GL_PERSPECTIVE_CORRECTION_HINT gives invalid_enum error on android. -// GLES20.glHint(GL10.GL_PERSPECTIVE_CORRECTION_HINT, GL10.GL_FASTEST); - -// checkGLError(); - useVBO = false; // NOTE: SDK_INT is only available since 1.6, @@ -1779,10 +1759,8 @@ private int convertWrapMode(Texture.WrapMode mode) { switch (mode) { -// case BorderClamp: -// return GLES20.GL_CLAMP_TO_BORDER; -// case Clamp: -// return GLES20.GL_CLAMP; + case BorderClamp: + case Clamp: case EdgeClamp: return GLES20.GL_CLAMP_TO_EDGE; case Repeat: @@ -2557,7 +2535,7 @@ logger.log(Level.INFO, "glDrawElements(), indexBuf.capacity ({0}), vertCount ({1})", new Object[]{indexBuf.getData().capacity(), vertCount}); } - GLES11.glDrawElements( + GLES20.glDrawElements( convertElementMode(mesh.getMode()), indexBuf.getData().capacity(), convertFormat(indexBuf.getFormat()), diff --git a/engine/src/android/com/jme3/renderer/android/TextureUtil.java b/engine/src/android/com/jme3/renderer/android/TextureUtil.java index 53b96b4..4a60d6b 100644 --- a/engine/src/android/com/jme3/renderer/android/TextureUtil.java +++ b/engine/src/android/com/jme3/renderer/android/TextureUtil.java
@@ -70,7 +70,7 @@ width /= 2; Bitmap bitmap2 = Bitmap.createScaledBitmap(bitmap, width, height, true); - bitmap.recycle(); + //bitmap.recycle(); bitmap = bitmap2; } } @@ -94,7 +94,7 @@ width = FastMath.nearestPowerOfTwo(width); height = FastMath.nearestPowerOfTwo(height); Bitmap bitmap2 = Bitmap.createScaledBitmap(bitmap, width, height, true); - bitmap.recycle(); + //bitmap.recycle(); bitmap = bitmap2; } }
diff --git a/engine/src/android/com/jme3/system/android/AndroidConfigChooser.java b/engine/src/android/com/jme3/system/android/AndroidConfigChooser.java index e55fa55..56ed415 100644 --- a/engine/src/android/com/jme3/system/android/AndroidConfigChooser.java +++ b/engine/src/android/com/jme3/system/android/AndroidConfigChooser.java
@@ -2,6 +2,7 @@ import android.graphics.PixelFormat; import android.opengl.GLSurfaceView.EGLConfigChooser; +import java.util.logging.Level; import java.util.logging.Logger; import javax.microedition.khronos.egl.EGL10; import javax.microedition.khronos.egl.EGLConfig; @@ -9,26 +10,23 @@ /** * AndroidConfigChooser is used to determine the best suited EGL Config - * @author larynx * + * @author larynx */ -public class AndroidConfigChooser implements EGLConfigChooser -{ +public class AndroidConfigChooser implements EGLConfigChooser { + private static final Logger logger = Logger.getLogger(AndroidConfigChooser.class.getName()); - protected int clientOpenGLESVersion = 0; protected EGLConfig bestConfig = null; protected EGLConfig fastestConfig = null; protected EGLConfig choosenConfig = null; protected ConfigType type; protected int pixelFormat; - protected boolean verbose = false; - private final static int EGL_OPENGL_ES2_BIT = 4; - public enum ConfigType - { + public enum ConfigType { + /** * RGB565, 0 alpha, 16 depth, 0 stencil */ @@ -39,94 +37,79 @@ BEST, /** * Turn off config chooser and use hardcoded - * setEGLContextClientVersion(2); - * setEGLConfigChooser(5, 6, 5, 0, 16, 0); + * setEGLContextClientVersion(2); setEGLConfigChooser(5, 6, 5, 0, 16, + * 0); */ LEGACY } - - public AndroidConfigChooser(ConfigType type, boolean verbose) - { + + public AndroidConfigChooser(ConfigType type) { this.type = type; - this.verbose = verbose; } - + /** * Gets called by the GLSurfaceView class to return the best config - */ + */ @Override - public EGLConfig chooseConfig(EGL10 egl, EGLDisplay display) - { + public EGLConfig chooseConfig(EGL10 egl, EGLDisplay display) { logger.info("GLSurfaceView asks for egl config, returning: "); logEGLConfig(choosenConfig, display, egl); return choosenConfig; } - + /** * findConfig is used to locate the best config and init the chooser with + * * @param egl * @param display * @return true if successfull, false if no config was found */ - public boolean findConfig(EGL10 egl, EGLDisplay display) - { - - if (type == ConfigType.BEST) - { - ComponentSizeChooser compChooser = new ComponentSizeChooser(8, 8, 8, 8, 32, 0); - choosenConfig = compChooser.chooseConfig(egl, display); + public boolean findConfig(EGL10 egl, EGLDisplay display) { - if (choosenConfig == null) - { + if (type == ConfigType.BEST) { + ComponentSizeChooser compChooser = new ComponentSizeChooser(8, 8, 8, 8, 32, 0); + choosenConfig = compChooser.chooseConfig(egl, display); + + if (choosenConfig == null) { compChooser = new ComponentSizeChooser(8, 8, 8, 0, 32, 0); choosenConfig = compChooser.chooseConfig(egl, display); - if (choosenConfig == null) - { + if (choosenConfig == null) { compChooser = new ComponentSizeChooser(8, 8, 8, 8, 16, 0); choosenConfig = compChooser.chooseConfig(egl, display); - if (choosenConfig == null) - { + if (choosenConfig == null) { compChooser = new ComponentSizeChooser(8, 8, 8, 0, 16, 0); choosenConfig = compChooser.chooseConfig(egl, display); } } - } - + } + logger.info("JME3 using best EGL configuration available here: "); - } - else - { - ComponentSizeChooser compChooser = new ComponentSizeChooser(5, 6, 5, 0, 16, 0); - choosenConfig = compChooser.chooseConfig(egl, display); + } else { + ComponentSizeChooser compChooser = new ComponentSizeChooser(5, 6, 5, 0, 16, 0); + choosenConfig = compChooser.chooseConfig(egl, display); logger.info("JME3 using fastest EGL configuration available here: "); } - - if (choosenConfig != null) - { - logger.info("JME3 using choosen config: "); - logEGLConfig(choosenConfig, display, egl); + + if (choosenConfig != null) { + logger.info("JME3 using choosen config: "); + logEGLConfig(choosenConfig, display, egl); pixelFormat = getPixelFormat(choosenConfig, display, egl); clientOpenGLESVersion = getOpenGLVersion(choosenConfig, display, egl); return true; - } - else - { + } else { logger.severe("###ERROR### Unable to get a valid OpenGL ES 2.0 config, nether Fastest nor Best found! Bug. Please report this."); clientOpenGLESVersion = 1; pixelFormat = PixelFormat.UNKNOWN; return false; - } + } } - - - private int getPixelFormat(EGLConfig conf, EGLDisplay display, EGL10 egl) - { + + private int getPixelFormat(EGLConfig conf, EGLDisplay display, EGL10 egl) { int[] value = new int[1]; - int result = PixelFormat.RGB_565; + int result = PixelFormat.RGB_565; egl.eglGetConfigAttrib(display, conf, EGL10.EGL_RED_SIZE, value); - if (value[0] == 8) - { + if (value[0] == 8) { result = PixelFormat.RGBA_8888; /* egl.eglGetConfigAttrib(display, conf, EGL10.EGL_ALPHA_SIZE, value); @@ -139,165 +122,163 @@ result = PixelFormat.RGB_888; }*/ } - - if (verbose) - { - logger.info("Using PixelFormat " + result); + + if (verbose) { + logger.log(Level.INFO, "Using PixelFormat {0}", result); } - + //return result; TODO Test pixelformat return PixelFormat.TRANSPARENT; } - - private int getOpenGLVersion(EGLConfig conf, EGLDisplay display, EGL10 egl) - { + + private int getOpenGLVersion(EGLConfig conf, EGLDisplay display, EGL10 egl) { int[] value = new int[1]; int result = 1; - + egl.eglGetConfigAttrib(display, conf, EGL10.EGL_RENDERABLE_TYPE, value); // Check if conf is OpenGL ES 2.0 - if ((value[0] & EGL_OPENGL_ES2_BIT) != 0) - { + if ((value[0] & EGL_OPENGL_ES2_BIT) != 0) { result = 2; } - return result; + return result; } - + /** * log output with egl config details + * * @param conf * @param display * @param egl */ - public void logEGLConfig(EGLConfig conf, EGLDisplay display, EGL10 egl) - { + public void logEGLConfig(EGLConfig conf, EGLDisplay display, EGL10 egl) { int[] value = new int[1]; egl.eglGetConfigAttrib(display, conf, EGL10.EGL_RED_SIZE, value); - logger.info(String.format("EGL_RED_SIZE = %d", value[0] ) ); - + logger.info(String.format("EGL_RED_SIZE = %d", value[0])); + egl.eglGetConfigAttrib(display, conf, EGL10.EGL_GREEN_SIZE, value); - logger.info(String.format("EGL_GREEN_SIZE = %d", value[0] ) ); - + logger.info(String.format("EGL_GREEN_SIZE = %d", value[0])); + egl.eglGetConfigAttrib(display, conf, EGL10.EGL_BLUE_SIZE, value); - logger.info(String.format("EGL_BLUE_SIZE = %d", value[0] ) ); - + logger.info(String.format("EGL_BLUE_SIZE = %d", value[0])); + egl.eglGetConfigAttrib(display, conf, EGL10.EGL_ALPHA_SIZE, value); - logger.info(String.format("EGL_ALPHA_SIZE = %d", value[0] ) ); - + logger.info(String.format("EGL_ALPHA_SIZE = %d", value[0])); + egl.eglGetConfigAttrib(display, conf, EGL10.EGL_DEPTH_SIZE, value); - logger.info(String.format("EGL_DEPTH_SIZE = %d", value[0] ) ); - + logger.info(String.format("EGL_DEPTH_SIZE = %d", value[0])); + egl.eglGetConfigAttrib(display, conf, EGL10.EGL_STENCIL_SIZE, value); - logger.info(String.format("EGL_STENCIL_SIZE = %d", value[0] ) ); + logger.info(String.format("EGL_STENCIL_SIZE = %d", value[0])); egl.eglGetConfigAttrib(display, conf, EGL10.EGL_RENDERABLE_TYPE, value); - logger.info(String.format("EGL_RENDERABLE_TYPE = %d", value[0] ) ); - + logger.info(String.format("EGL_RENDERABLE_TYPE = %d", value[0])); + egl.eglGetConfigAttrib(display, conf, EGL10.EGL_SURFACE_TYPE, value); - logger.info(String.format("EGL_SURFACE_TYPE = %d", value[0] ) ); + logger.info(String.format("EGL_SURFACE_TYPE = %d", value[0])); } - - public int getClientOpenGLESVersion() - { + + public int getClientOpenGLESVersion() { return clientOpenGLESVersion; } - public void setClientOpenGLESVersion(int clientOpenGLESVersion) - { + public void setClientOpenGLESVersion(int clientOpenGLESVersion) { this.clientOpenGLESVersion = clientOpenGLESVersion; } - - public int getPixelFormat() - { + + public int getPixelFormat() { return pixelFormat; } - - - - private abstract class BaseConfigChooser implements EGLConfigChooser - { + + private abstract class BaseConfigChooser implements EGLConfigChooser { + private boolean bClientOpenGLESVersionSet; - - public BaseConfigChooser(int[] configSpec) - { - bClientOpenGLESVersionSet = false; - mConfigSpec = filterConfigSpec(configSpec); - } - - public EGLConfig chooseConfig(EGL10 egl, EGLDisplay display) - { - int[] num_config = new int[1]; - if (!egl.eglChooseConfig(display, mConfigSpec, null, 0, - num_config)) { - throw new IllegalArgumentException("eglChooseConfig failed"); - } - - int numConfigs = num_config[0]; - - if (numConfigs <= 0) - { - //throw new IllegalArgumentException("No configs match configSpec"); - - return null; - } - - EGLConfig[] configs = new EGLConfig[numConfigs]; - if (!egl.eglChooseConfig(display, mConfigSpec, configs, numConfigs, - num_config)) { - throw new IllegalArgumentException("eglChooseConfig#2 failed"); - } - EGLConfig config = chooseConfig(egl, display, configs); - //if (config == null) { - // throw new IllegalArgumentException("No config chosen"); - //} - return config; - } - - abstract EGLConfig chooseConfig(EGL10 egl, EGLDisplay display, - EGLConfig[] configs); - - protected int[] mConfigSpec; - - private int[] filterConfigSpec(int[] configSpec) - { - if (bClientOpenGLESVersionSet == true) { - return configSpec; - } - /* We know none of the subclasses define EGL_RENDERABLE_TYPE. - * And we know the configSpec is well formed. - */ - int len = configSpec.length; - int[] newConfigSpec = new int[len + 2]; - System.arraycopy(configSpec, 0, newConfigSpec, 0, len-1); - newConfigSpec[len-1] = EGL10.EGL_RENDERABLE_TYPE; - newConfigSpec[len] = 4; /* EGL_OPENGL_ES2_BIT */ - newConfigSpec[len+1] = EGL10.EGL_NONE; - - bClientOpenGLESVersionSet = true; - - return newConfigSpec; - } + + public BaseConfigChooser(int[] configSpec) { + bClientOpenGLESVersionSet = false; + mConfigSpec = filterConfigSpec(configSpec); + } + + public EGLConfig chooseConfig(EGL10 egl, EGLDisplay display) { + int[] num_config = new int[1]; + if (!egl.eglChooseConfig(display, mConfigSpec, null, 0, + num_config)) { + throw new IllegalArgumentException("eglChooseConfig failed"); + } + + int numConfigs = num_config[0]; + + if (numConfigs <= 0) { + //throw new IllegalArgumentException("No configs match configSpec"); + + return null; + } + + EGLConfig[] configs = new EGLConfig[numConfigs]; + if (!egl.eglChooseConfig(display, mConfigSpec, configs, numConfigs, + num_config)) { + throw new IllegalArgumentException("eglChooseConfig#2 failed"); + } + EGLConfig config = chooseConfig(egl, display, configs); + //if (config == null) { + // throw new IllegalArgumentException("No config chosen"); + //} + return config; + } + + abstract EGLConfig chooseConfig(EGL10 egl, EGLDisplay display, + EGLConfig[] configs); + protected int[] mConfigSpec; + + private int[] filterConfigSpec(int[] configSpec) { + if (bClientOpenGLESVersionSet == true) { + return configSpec; + } + /* + * We know none of the subclasses define EGL_RENDERABLE_TYPE. And we + * know the configSpec is well formed. + */ + int len = configSpec.length; + int[] newConfigSpec = new int[len + 2]; + System.arraycopy(configSpec, 0, newConfigSpec, 0, len - 1); + newConfigSpec[len - 1] = EGL10.EGL_RENDERABLE_TYPE; + newConfigSpec[len] = 4; /* + * EGL_OPENGL_ES2_BIT + */ + newConfigSpec[len + 1] = EGL10.EGL_NONE; + + bClientOpenGLESVersionSet = true; + + return newConfigSpec; + } } - + /** - * Choose a configuration with exactly the specified r,g,b,a sizes, - * and at least the specified depth and stencil sizes. + * Choose a configuration with exactly the specified r,g,b,a sizes, and at + * least the specified depth and stencil sizes. */ - private class ComponentSizeChooser extends BaseConfigChooser - { + private class ComponentSizeChooser extends BaseConfigChooser { + + private int[] mValue; + // Subclasses can adjust these values: + protected int mRedSize; + protected int mGreenSize; + protected int mBlueSize; + protected int mAlphaSize; + protected int mDepthSize; + protected int mStencilSize; + public ComponentSizeChooser(int redSize, int greenSize, int blueSize, - int alphaSize, int depthSize, int stencilSize) - { - super(new int[] { - EGL10.EGL_RED_SIZE, redSize, - EGL10.EGL_GREEN_SIZE, greenSize, - EGL10.EGL_BLUE_SIZE, blueSize, - EGL10.EGL_ALPHA_SIZE, alphaSize, - EGL10.EGL_DEPTH_SIZE, depthSize, - EGL10.EGL_STENCIL_SIZE, stencilSize, - EGL10.EGL_NONE}); + int alphaSize, int depthSize, int stencilSize) { + super(new int[]{ + EGL10.EGL_RED_SIZE, redSize, + EGL10.EGL_GREEN_SIZE, greenSize, + EGL10.EGL_BLUE_SIZE, blueSize, + EGL10.EGL_ALPHA_SIZE, alphaSize, + EGL10.EGL_DEPTH_SIZE, depthSize, + EGL10.EGL_STENCIL_SIZE, stencilSize, + EGL10.EGL_NONE}); mValue = new int[1]; mRedSize = redSize; mGreenSize = greenSize; @@ -305,25 +286,22 @@ mAlphaSize = alphaSize; mDepthSize = depthSize; mStencilSize = stencilSize; - } + } @Override - public EGLConfig chooseConfig(EGL10 egl, EGLDisplay display, EGLConfig[] configs) - { - for (EGLConfig config : configs) - { + public EGLConfig chooseConfig(EGL10 egl, EGLDisplay display, EGLConfig[] configs) { + for (EGLConfig config : configs) { int d = findConfigAttrib(egl, display, config, EGL10.EGL_DEPTH_SIZE, 0); int s = findConfigAttrib(egl, display, config, EGL10.EGL_STENCIL_SIZE, 0); - if ((d >= mDepthSize) && (s >= mStencilSize)) - { + if ((d >= mDepthSize) && (s >= mStencilSize)) { int r = findConfigAttrib(egl, display, config, EGL10.EGL_RED_SIZE, 0); int g = findConfigAttrib(egl, display, config, - EGL10.EGL_GREEN_SIZE, 0); + EGL10.EGL_GREEN_SIZE, 0); int b = findConfigAttrib(egl, display, config, - EGL10.EGL_BLUE_SIZE, 0); + EGL10.EGL_BLUE_SIZE, 0); int a = findConfigAttrib(egl, display, config, EGL10.EGL_ALPHA_SIZE, 0); if ((r == mRedSize) && (g == mGreenSize) @@ -336,27 +314,12 @@ } private int findConfigAttrib(EGL10 egl, EGLDisplay display, - EGLConfig config, int attribute, int defaultValue) - { + EGLConfig config, int attribute, int defaultValue) { - if (egl.eglGetConfigAttrib(display, config, attribute, mValue)) - { + if (egl.eglGetConfigAttrib(display, config, attribute, mValue)) { return mValue[0]; } return defaultValue; } - - private int[] mValue; - // Subclasses can adjust these values: - protected int mRedSize; - protected int mGreenSize; - protected int mBlueSize; - protected int mAlphaSize; - protected int mDepthSize; - protected int mStencilSize; } - - - - } diff --git a/engine/src/android/com/jme3/system/android/OGLESContext.java b/engine/src/android/com/jme3/system/android/OGLESContext.java index 668b68d..1ecf2e1 100644 --- a/engine/src/android/com/jme3/system/android/OGLESContext.java +++ b/engine/src/android/com/jme3/system/android/OGLESContext.java
@@ -32,26 +32,37 @@ package com.jme3.system.android; import android.app.Activity; +import android.app.AlertDialog; import android.content.Context; +import android.content.DialogInterface; import android.opengl.GLSurfaceView; +import android.text.InputType; +import android.view.Gravity; import android.view.SurfaceHolder; +import android.view.ViewGroup.LayoutParams; +import android.widget.EditText; +import android.widget.FrameLayout; import com.jme3.app.AndroidHarness; import com.jme3.app.Application; import com.jme3.input.JoyInput; import com.jme3.input.KeyInput; import com.jme3.input.MouseInput; +import com.jme3.input.SoftTextDialogInput; import com.jme3.input.TouchInput; import com.jme3.input.android.AndroidInput; +import com.jme3.input.controls.SoftTextDialogInputListener; import com.jme3.input.controls.TouchTrigger; import com.jme3.input.dummy.DummyKeyInput; import com.jme3.input.dummy.DummyMouseInput; import com.jme3.renderer.android.OGLESShaderRenderer; import com.jme3.system.AppSettings; import com.jme3.system.JmeContext; +import com.jme3.system.JmeSystem; import com.jme3.system.SystemListener; import com.jme3.system.Timer; import com.jme3.system.android.AndroidConfigChooser.ConfigType; import java.util.concurrent.atomic.AtomicBoolean; +import java.util.logging.Level; import java.util.logging.Logger; import javax.microedition.khronos.egl.EGL10; import javax.microedition.khronos.egl.EGLConfig; @@ -59,7 +70,7 @@ import javax.microedition.khronos.egl.EGLDisplay; import javax.microedition.khronos.opengles.GL10; -public class OGLESContext implements JmeContext, GLSurfaceView.Renderer { +public class OGLESContext implements JmeContext, GLSurfaceView.Renderer, SoftTextDialogInput { private static final Logger logger = Logger.getLogger(OGLESContext.class.getName()); protected final AtomicBoolean created = new AtomicBoolean(false); @@ -76,7 +87,6 @@ protected boolean autoFlush = true; protected AndroidInput view; private boolean firstDrawFrame = true; - //protected int minFrameDuration = 1000 / frameRate; // Set a max FPS of 33 protected int minFrameDuration = 0; // No FPS cap /** @@ -145,12 +155,12 @@ int[] version = new int[2]; if (egl.eglInitialize(display, version) == true) { - logger.info("Display EGL Version: " + version[0] + "." + version[1]); + logger.log(Level.INFO, "Display EGL Version: {0}.{1}", new Object[]{version[0], version[1]}); } try { // Create a config chooser - AndroidConfigChooser configChooser = new AndroidConfigChooser(configType, eglConfigVerboseLogging); + AndroidConfigChooser configChooser = new AndroidConfigChooser(configType); // Init chooser if (!configChooser.findConfig(egl, display)) { listener.handleError("Unable to find suitable EGL config", null); @@ -162,7 +172,7 @@ listener.handleError("OpenGL ES 2.0 is not supported on this device", null); return null; } - + // Requesting client version from GLSurfaceView which is extended by // AndroidInput. view.setEGLContextClientVersion(clientOpenGLESVersion); @@ -210,12 +220,14 @@ // Setup unhandled Exception Handler if (ctx instanceof AndroidHarness) { Thread.currentThread().setUncaughtExceptionHandler(new Thread.UncaughtExceptionHandler() { + public void uncaughtException(Thread thread, Throwable thrown) { ((AndroidHarness) ctx).handleError("Exception thrown in " + thread.toString(), thrown); } }); } else { Thread.currentThread().setUncaughtExceptionHandler(new Thread.UncaughtExceptionHandler() { + public void uncaughtException(Thread thread, Throwable thrown) { listener.handleError("Exception thrown in " + thread.toString(), thrown); } @@ -244,6 +256,8 @@ } } + JmeSystem.setSoftTextDialogInput(this); + needClose.set(false); renderable.set(true); } @@ -268,6 +282,13 @@ logger.info("Display destroyed."); renderable.set(false); + final Context ctx = this.view.getContext(); + if (ctx instanceof AndroidHarness) { + AndroidHarness harness = (AndroidHarness) ctx; + if (harness.isFinishOnAppStop()) { + harness.finish(); + } + } } } @@ -442,4 +463,67 @@ public int getClientOpenGLESVersion() { return clientOpenGLESVersion; } + + public void requestDialog(final int id, final String title, final String initialValue, final SoftTextDialogInputListener listener) { + logger.log(Level.INFO, "requestDialog: title: {0}, initialValue: {1}", + new Object[]{title, initialValue}); + + JmeAndroidSystem.getActivity().runOnUiThread(new Runnable() { + + @Override + public void run() { + + final FrameLayout layoutTextDialogInput = new FrameLayout(JmeAndroidSystem.getActivity()); + final EditText editTextDialogInput = new EditText(JmeAndroidSystem.getActivity()); + editTextDialogInput.setWidth(LayoutParams.FILL_PARENT); + editTextDialogInput.setHeight(LayoutParams.FILL_PARENT); + editTextDialogInput.setPadding(20, 20, 20, 20); + editTextDialogInput.setGravity(Gravity.FILL_HORIZONTAL); + + editTextDialogInput.setText(initialValue); + + switch (id) { + case SoftTextDialogInput.TEXT_ENTRY_DIALOG: + + editTextDialogInput.setInputType(InputType.TYPE_CLASS_TEXT); + break; + + case SoftTextDialogInput.NUMERIC_ENTRY_DIALOG: + + editTextDialogInput.setInputType(InputType.TYPE_CLASS_NUMBER | InputType.TYPE_NUMBER_FLAG_DECIMAL | InputType.TYPE_NUMBER_FLAG_SIGNED); + break; + + case SoftTextDialogInput.NUMERIC_KEYPAD_DIALOG: + + editTextDialogInput.setInputType(InputType.TYPE_CLASS_PHONE); + break; + + default: + break; + } + + layoutTextDialogInput.addView(editTextDialogInput); + + AlertDialog dialogTextInput = new AlertDialog.Builder(JmeAndroidSystem.getActivity()).setTitle(title).setView(layoutTextDialogInput).setPositiveButton("OK", + new DialogInterface.OnClickListener() { + + public void onClick(DialogInterface dialog, int whichButton) { + /* User clicked OK, send COMPLETE action + * and text */ + listener.onSoftText(SoftTextDialogInputListener.COMPLETE, editTextDialogInput.getText().toString()); + } + }).setNegativeButton("Cancel", + new DialogInterface.OnClickListener() { + + public void onClick(DialogInterface dialog, int whichButton) { + /* User clicked CANCEL, send CANCEL action + * and text */ + listener.onSoftText(SoftTextDialogInputListener.CANCEL, editTextDialogInput.getText().toString()); + } + }).create(); + + dialogTextInput.show(); + } + }); + } } diff --git a/engine/src/android/com/jme3/util/AndroidLogHandler.java b/engine/src/android/com/jme3/util/AndroidLogHandler.java index 8fb21c2..1da1299 100644 --- a/engine/src/android/com/jme3/util/AndroidLogHandler.java +++ b/engine/src/android/com/jme3/util/AndroidLogHandler.java
@@ -4,7 +4,12 @@ import java.util.logging.Handler; import java.util.logging.Level; import java.util.logging.LogRecord; +import java.util.logging.Logger; +/** + * Converts from Java based logging ({@link Logger} to Android based + * logging {@link Log}. + */ public class AndroidLogHandler extends Handler { @Override @@ -21,17 +26,16 @@ String clsName = record.getSourceClassName(); String msg = record.getMessage(); Throwable t = record.getThrown(); - if (level == Level.INFO){ + if (level == Level.INFO) { Log.i(clsName, msg, t); - }else if (level == Level.SEVERE){ + } else if (level == Level.SEVERE) { Log.e(clsName, msg, t); - }else if (level == Level.WARNING){ + } else if (level == Level.WARNING) { Log.w(clsName, msg, t); - }else if (level == Level.CONFIG){ + } else if (level == Level.CONFIG) { Log.d(clsName, msg, t); - }else if (level == Level.FINE || level == Level.FINER || level == Level.FINEST){ + } else if (level == Level.FINE || level == Level.FINER || level == Level.FINEST) { Log.v(clsName, msg, t); } } - } diff --git a/engine/src/android/com/jme3/util/RingBuffer.java b/engine/src/android/com/jme3/util/RingBuffer.java index 786417b..1d3c22d 100644 --- a/engine/src/android/com/jme3/util/RingBuffer.java +++ b/engine/src/android/com/jme3/util/RingBuffer.java
@@ -4,20 +4,21 @@ import java.util.NoSuchElementException; /** - * Ring buffer (fixed size queue) implementation using a circular array (array with wrap-around). + * Ring buffer (fixed size queue) implementation using a circular array (array + * with wrap-around). */ // suppress unchecked warnings in Java 1.5.0_6 and later @SuppressWarnings("unchecked") -public class RingBuffer<Item> implements Iterable<Item> { +public class RingBuffer<T> implements Iterable<T> { - private Item[] buffer; // queue elements + private T[] buffer; // queue elements private int count = 0; // number of elements on queue private int indexOut = 0; // index of first element of queue private int indexIn = 0; // index of next available slot // cast needed since no generic array creation in Java public RingBuffer(int capacity) { - buffer = (Item[]) new Object[capacity]; + buffer = (T[]) new Object[capacity]; } public boolean isEmpty() { @@ -28,7 +29,7 @@ return count; } - public void push(Item item) { + public void push(T item) { if (count == buffer.length) { throw new RuntimeException("Ring buffer overflow"); } @@ -37,23 +38,23 @@ count++; } - public Item pop() { + public T pop() { if (isEmpty()) { throw new RuntimeException("Ring buffer underflow"); } - Item item = buffer[indexOut]; + T item = buffer[indexOut]; buffer[indexOut] = null; // to help with garbage collection count--; indexOut = (indexOut + 1) % buffer.length; // wrap-around return item; } - public Iterator<Item> iterator() { + public Iterator<T> iterator() { return new RingBufferIterator(); } // an iterator, doesn't implement remove() since it's optional - private class RingBufferIterator implements Iterator<Item> { + private class RingBufferIterator implements Iterator<T> { private int i = 0; @@ -65,7 +66,7 @@ throw new UnsupportedOperationException(); } - public Item next() { + public T next() { if (!hasNext()) { throw new NoSuchElementException(); } diff --git a/engine/src/android/res/layout/about.xml b/engine/src/android/res/layout/about.xml index 1d8c2b0..ca249e7 100644 --- a/engine/src/android/res/layout/about.xml +++ b/engine/src/android/res/layout/about.xml
@@ -1,29 +1,29 @@ <?xml version="1.0" encoding="utf-8"?> <LinearLayout - xmlns:android="http://schemas.android.com/apk/res/android" - android:orientation="vertical" - android:layout_width="fill_parent" - android:layout_height="fill_parent" + xmlns:android="http://schemas.android.com/apk/res/android" + android:orientation="vertical" + android:layout_width="fill_parent" + android:layout_height="fill_parent" > <LinearLayout - android:id="@+id/buttonsContainer" - xmlns:android="http://schemas.android.com/apk/res/android" - android:orientation="vertical" - android:layout_width="fill_parent" - android:layout_height="fill_parent" - > + android:id="@+id/buttonsContainer" + xmlns:android="http://schemas.android.com/apk/res/android" + android:orientation="vertical" + android:layout_width="fill_parent" + android:layout_height="fill_parent" + > <TextView - android:layout_width="fill_parent" - android:layout_height="wrap_content" - android:text="copyright (c) 2009-2010 JMonkeyEngine" + android:layout_width="fill_parent" + android:layout_height="wrap_content" + android:text="copyright (c) 2009-2010 JMonkeyEngine" /> <TextView - android:layout_width="fill_parent" - android:layout_height="wrap_content" - android:text="http://www.jmonkeyengine.org" + android:layout_width="fill_parent" + android:layout_height="wrap_content" + android:text="http://www.jmonkeyengine.org" /> </LinearLayout>
diff --git a/engine/src/android/res/layout/tests.xml b/engine/src/android/res/layout/tests.xml index 70bd5ec..f38e233 100644 --- a/engine/src/android/res/layout/tests.xml +++ b/engine/src/android/res/layout/tests.xml
@@ -1,17 +1,17 @@ <?xml version="1.0" encoding="utf-8"?> <LinearLayout - xmlns:android="http://schemas.android.com/apk/res/android" - android:orientation="vertical" - android:layout_width="fill_parent" - android:layout_height="fill_parent" + xmlns:android="http://schemas.android.com/apk/res/android" + android:orientation="vertical" + android:layout_width="fill_parent" + android:layout_height="fill_parent" > <LinearLayout - android:id="@+id/buttonsContainer" - xmlns:android="http://schemas.android.com/apk/res/android" - android:orientation="vertical" - android:layout_width="fill_parent" - android:layout_height="fill_parent"> + android:id="@+id/buttonsContainer" + xmlns:android="http://schemas.android.com/apk/res/android" + android:orientation="vertical" + android:layout_width="fill_parent" + android:layout_height="fill_parent"> <!-- <Button android:id="@+id/SimpleTextured"
diff --git a/engine/src/blender/com/jme3/scene/plugins/blender/meshes/MeshHelper.java b/engine/src/blender/com/jme3/scene/plugins/blender/meshes/MeshHelper.java index 484e942..7f9e8ac 100644 --- a/engine/src/blender/com/jme3/scene/plugins/blender/meshes/MeshHelper.java +++ b/engine/src/blender/com/jme3/scene/plugins/blender/meshes/MeshHelper.java
@@ -31,6 +31,14 @@ */ package com.jme3.scene.plugins.blender.meshes; +import java.nio.ByteBuffer; +import java.util.ArrayList; +import java.util.HashMap; +import java.util.LinkedList; +import java.util.List; +import java.util.Map; +import java.util.Map.Entry; + import com.jme3.asset.BlenderKey.FeaturesToLoad; import com.jme3.material.Material; import com.jme3.math.FastMath; @@ -57,10 +65,6 @@ import com.jme3.scene.plugins.blender.textures.UVCoordinatesGenerator; import com.jme3.texture.Texture; import com.jme3.util.BufferUtils; -import java.nio.ByteBuffer; -import java.nio.FloatBuffer; -import java.util.*; -import java.util.Map.Entry; /** * A class that is used in mesh calculations. @@ -300,12 +304,13 @@ // generating meshes //FloatBuffer verticesColorsBuffer = this.createFloatBuffer(verticesColors); ByteBuffer verticesColorsBuffer = createByteBuffer(verticesColors); + verticesAmount = vertexList.size(); for (Entry<Integer, List<Integer>> meshEntry : meshesMap.entrySet()) { Mesh mesh = new Mesh(); // creating vertices indices for this mesh List<Integer> indexList = meshEntry.getValue(); - if(verticesAmount < Short.MAX_VALUE * 2) { + if(verticesAmount <= Short.MAX_VALUE) { short[] indices = new short[indexList.size()]; for (int i = 0; i < indexList.size(); ++i) { indices[i] = indexList.get(i).shortValue(); diff --git a/engine/src/blender/com/jme3/scene/plugins/blender/textures/blending/TextureBlenderAWT.java b/engine/src/blender/com/jme3/scene/plugins/blender/textures/blending/TextureBlenderAWT.java index 75fc0c5..918072e 100644 --- a/engine/src/blender/com/jme3/scene/plugins/blender/textures/blending/TextureBlenderAWT.java +++ b/engine/src/blender/com/jme3/scene/plugins/blender/textures/blending/TextureBlenderAWT.java
@@ -56,8 +56,8 @@ float[] resultPixel = new float[4]; int dataIndex = 0; while (data.hasRemaining()) { - float tin = this.setupMaterialColor(data, format, neg, pixelColor); - this.blendPixel(resultPixel, materialColor, color, tin, blendType, blenderContext); + this.setupMaterialColor(data, format, neg, pixelColor); + this.blendPixel(resultPixel, materialColor, pixelColor, affectFactor, blendType, blenderContext); newData.put(dataIndex++, (byte) (resultPixel[0] * 255.0f)); newData.put(dataIndex++, (byte) (resultPixel[1] * 255.0f)); newData.put(dataIndex++, (byte) (resultPixel[2] * 255.0f)); diff --git a/engine/src/core-data/Common/MatDefs/Light/Lighting.frag b/engine/src/core-data/Common/MatDefs/Light/Lighting.frag index 76f0e15..eb1c3fd 100644 --- a/engine/src/core-data/Common/MatDefs/Light/Lighting.frag +++ b/engine/src/core-data/Common/MatDefs/Light/Lighting.frag
@@ -126,6 +126,10 @@ float att = vLightDir.w; #endif + if (m_Shininess <= 1.0) { + specularFactor = 0.0; // should be one instruction on most cards .. + } + specularFactor *= diffuseFactor; return vec2(diffuseFactor, specularFactor) * vec2(att); diff --git a/engine/src/core-data/Common/MatDefs/Misc/Particle.j3md b/engine/src/core-data/Common/MatDefs/Misc/Particle.j3md index e28d41a..73674f9 100644 --- a/engine/src/core-data/Common/MatDefs/Misc/Particle.j3md +++ b/engine/src/core-data/Common/MatDefs/Misc/Particle.j3md
@@ -4,6 +4,10 @@ Texture2D Texture Float Quadratic Boolean PointSprite + + //only used for soft particles + Texture2D DepthTexture + Float Softness // Texture of the glowing parts of the material Texture2D GlowMap @@ -58,6 +62,28 @@ } } + Technique SoftParticles{ + + VertexShader GLSL100 : Common/MatDefs/Misc/SoftParticle.vert + FragmentShader GLSL100 : Common/MatDefs/Misc/SoftParticle.frag + + WorldParameters { + WorldViewProjectionMatrix + WorldViewMatrix + WorldMatrix + CameraPosition + } + + RenderState { + Blend AlphaAdditive + DepthWrite Off + } + + Defines { + USE_TEXTURE : Texture + } + } + Technique FixedFunc { RenderState { Blend AlphaAdditive diff --git a/engine/src/core-data/Common/MatDefs/Misc/SoftParticle.frag b/engine/src/core-data/Common/MatDefs/Misc/SoftParticle.frag new file mode 100644 index 0000000..d3108b5 --- /dev/null +++ b/engine/src/core-data/Common/MatDefs/Misc/SoftParticle.frag
@@ -0,0 +1,50 @@ +uniform sampler2D m_DepthTexture; +uniform float m_Softness; // Power used in the contrast function +varying vec2 vPos; // Position of the pixel +varying vec2 projPos;// z and w valus in projection space + +#ifdef USE_TEXTURE +uniform sampler2D m_Texture; +varying vec4 texCoord; +#endif + +varying vec4 color; + +float Contrast(float d){ + float val = clamp( 2.0*( (d > 0.5) ? 1.0-d : d ), 0.0, 1.0); + float a = 0.5 * pow(val, m_Softness); + return (d > 0.5) ? 1.0 - a : a; +} + +float stdDiff(float d){ + return clamp((d)*m_Softness,0.0,1.0); +} + + +void main(){ + if (color.a <= 0.01) + discard; + + vec4 c = vec4(1.0,1.0,1.0,1.0);//color; + #ifdef USE_TEXTURE + #ifdef POINT_SPRITE + vec2 uv = mix(texCoord.xy, texCoord.zw, gl_PointCoord.xy); + #else + vec2 uv = texCoord.xy; + #endif + c = texture2D(m_Texture, uv) * color; + #endif + + + float depthv = texture2D(m_DepthTexture, vPos).x*2.0-1.0; // Scene depth + depthv*=projPos.y; + float particleDepth = projPos.x; + + float zdiff =depthv-particleDepth; + if(zdiff<=0.0){ + discard; + } + // Computes alpha based on the particles distance to the rest of the scene + c.a = c.a * stdDiff(zdiff);// Contrast(zdiff); + gl_FragColor =c; +} \ No newline at end of file diff --git a/engine/src/core-data/Common/MatDefs/Misc/SoftParticle.vert b/engine/src/core-data/Common/MatDefs/Misc/SoftParticle.vert new file mode 100644 index 0000000..4f0dfd5 --- /dev/null +++ b/engine/src/core-data/Common/MatDefs/Misc/SoftParticle.vert
@@ -0,0 +1,53 @@ +uniform mat4 g_WorldViewProjectionMatrix; + +attribute vec3 inPosition; +attribute vec4 inColor; +attribute vec4 inTexCoord; + +varying vec4 color; +// z and w values in projection space +varying vec2 projPos; +varying vec2 vPos; // Position of the pixel in clip space + + + +#ifdef USE_TEXTURE +varying vec4 texCoord; +#endif + +#ifdef POINT_SPRITE +uniform mat4 g_WorldViewMatrix; +uniform mat4 g_WorldMatrix; +uniform vec3 g_CameraPosition; +uniform float m_Quadratic; +const float SIZE_MULTIPLIER = 4.0; +attribute float inSize; +#endif + +void main(){ + vec4 pos = vec4(inPosition, 1.0); + + gl_Position = g_WorldViewProjectionMatrix * pos; + color = inColor; + + projPos = gl_Position.zw; + // projPos.x = 0.5 * (projPos.x) + 0.5; + + // Transforms the vPosition data to the range [0,1] + vPos = (gl_Position.xy / gl_Position.w + 1.0) / 2.0; + + #ifdef USE_TEXTURE + texCoord = inTexCoord; + #endif + + #ifdef POINT_SPRITE + vec4 worldPos = g_WorldMatrix * pos; + float d = distance(g_CameraPosition.xyz, worldPos.xyz); + gl_PointSize = max(1.0, (inSize * SIZE_MULTIPLIER * m_Quadratic) / d); + + //vec4 worldViewPos = g_WorldViewMatrix * pos; + //gl_PointSize = (inSize * SIZE_MULTIPLIER * m_Quadratic)*100.0 / worldViewPos.z; + + color.a *= min(gl_PointSize, 1.0); + #endif +} \ No newline at end of file diff --git a/engine/src/core-effects/com/jme3/post/filters/TranslucentBucketFilter.java b/engine/src/core-effects/com/jme3/post/filters/TranslucentBucketFilter.java index 47be413..c377a58 100644 --- a/engine/src/core-effects/com/jme3/post/filters/TranslucentBucketFilter.java +++ b/engine/src/core-effects/com/jme3/post/filters/TranslucentBucketFilter.java
@@ -5,6 +5,7 @@ package com.jme3.post.filters; import com.jme3.asset.AssetManager; +import com.jme3.effect.ParticleEmitter; import com.jme3.material.Material; import com.jme3.math.ColorRGBA; import com.jme3.post.Filter; @@ -12,8 +13,14 @@ import com.jme3.renderer.Renderer; import com.jme3.renderer.ViewPort; import com.jme3.renderer.queue.RenderQueue; +import com.jme3.scene.Node; + +import com.jme3.scene.Spatial; import com.jme3.texture.FrameBuffer; +import com.jme3.texture.Texture; import com.jme3.texture.Texture2D; +import java.util.logging.Level; +import java.util.logging.Logger; /** * A filter to handle translucent objects when rendering a scene with filters that uses depth like WaterFilter and SSAOFilter @@ -22,11 +29,25 @@ */ public final class TranslucentBucketFilter extends Filter { + private final static Logger logger = Logger.getLogger(TranslucentBucketFilter.class.getName()); private RenderManager renderManager; + private boolean enabledSoftParticles = false; + private Texture depthTexture; + private ViewPort viewPort; + + public TranslucentBucketFilter() { + super("TranslucentBucketFilter"); + } + + public TranslucentBucketFilter(boolean enabledSoftParticles) { + this(); + this.enabledSoftParticles = enabledSoftParticles; + } @Override protected void initFilter(AssetManager manager, RenderManager rm, ViewPort vp, int w, int h) { this.renderManager = rm; + this.viewPort = vp; material = new Material(manager, "Common/MatDefs/Post/Overlay.j3md"); material.setColor("Color", ColorRGBA.White); Texture2D tex = processor.getFilterTexture(); @@ -37,6 +58,26 @@ material.clearParam("NumSamples"); } renderManager.setHandleTranslucentBucket(false); + if (enabledSoftParticles && depthTexture != null) { + initSoftParticles(vp, true); + } + } + + private void initSoftParticles(ViewPort vp, boolean enabledSP) { + if (depthTexture != null) { + for (Spatial scene : vp.getScenes()) { + makeSoftParticleEmitter(scene, enabledSP && enabled); + } + } + + } + + @Override + protected void setDepthTexture(Texture depthTexture) { + this.depthTexture = depthTexture; + if (enabledSoftParticles && depthTexture != null) { + initSoftParticles(viewPort, true); + } } /** @@ -49,6 +90,11 @@ } @Override + protected boolean isRequiresDepthTexture() { + return enabledSoftParticles; + } + + @Override protected void postFrame(RenderManager renderManager, ViewPort viewPort, FrameBuffer prevFilterBuffer, FrameBuffer sceneBuffer) { renderManager.setCamera(viewPort.getCamera(), false); if (prevFilterBuffer != sceneBuffer) { @@ -63,6 +109,8 @@ if (renderManager != null) { renderManager.setHandleTranslucentBucket(true); } + + initSoftParticles(viewPort, false); } @Override @@ -76,5 +124,32 @@ if (renderManager != null) { renderManager.setHandleTranslucentBucket(!enabled); } + initSoftParticles(viewPort, enabledSoftParticles); + } + + private void makeSoftParticleEmitter(Spatial scene, boolean enabled) { + if (scene instanceof Node) { + Node n = (Node) scene; + for (Spatial child : n.getChildren()) { + makeSoftParticleEmitter(child, enabled); + } + } + if (scene instanceof ParticleEmitter) { + ParticleEmitter emitter = (ParticleEmitter) scene; + if (enabled) { + enabledSoftParticles = enabled; + + emitter.getMaterial().selectTechnique("SoftParticles", renderManager); + emitter.getMaterial().setTexture("DepthTexture", processor.getDepthTexture()); + emitter.setQueueBucket(RenderQueue.Bucket.Translucent); + + logger.log(Level.INFO, "Made particle Emitter {0} soft.", emitter.getName()); + } else { + emitter.getMaterial().clearParam("DepthTexture"); + emitter.getMaterial().selectTechnique("Default", renderManager); + // emitter.setQueueBucket(RenderQueue.Bucket.Transparent); + logger.log(Level.INFO, "Particle Emitter {0} is not soft anymore.", emitter.getName()); + } + } } } diff --git a/engine/src/core-plugins/com/jme3/asset/plugins/HttpZipLocator.java~ b/engine/src/core-plugins/com/jme3/asset/plugins/HttpZipLocator.java~ deleted file mode 100644 index f5fbfd1..0000000 --- a/engine/src/core-plugins/com/jme3/asset/plugins/HttpZipLocator.java~ +++ /dev/null
@@ -1,355 +0,0 @@ -/* - * Copyright (c) 2009-2010 jMonkeyEngine - * All rights reserved. - * - * Redistribution and use in source and binary forms, with or without - * modification, are permitted provided that the following conditions are - * met: - * - * * Redistributions of source code must retain the above copyright - * notice, this list of conditions and the following disclaimer. - * - * * Redistributions in binary form must reproduce the above copyright - * notice, this list of conditions and the following disclaimer in the - * documentation and/or other materials provided with the distribution. - * - * * Neither the name of 'jMonkeyEngine' nor the names of its contributors - * may be used to endorse or promote products derived from this software - * without specific prior written permission. - * - * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS - * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED - * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR - * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR - * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, - * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, - * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR - * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF - * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING - * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS - * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. - */ - -package com.jme3.asset.plugins; - -import com.jme3.asset.AssetInfo; -import com.jme3.asset.AssetKey; -import com.jme3.asset.AssetLocator; -import com.jme3.asset.AssetManager; -import java.io.IOException; -import java.io.InputStream; -import java.net.HttpURLConnection; -import java.net.URL; -import java.nio.ByteBuffer; -import java.nio.CharBuffer; -import java.nio.charset.CharacterCodingException; -import java.nio.charset.Charset; -import java.nio.charset.CharsetDecoder; -import java.nio.charset.CoderResult; -import java.util.HashMap; -import java.util.logging.Level; -import java.util.logging.Logger; -import java.util.zip.Inflater; -import java.util.zip.InflaterInputStream; -import java.util.zip.ZipEntry; - -public class HttpZipLocator implements AssetLocator { - - private static final Logger logger = Logger.getLogger(HttpZipLocator.class.getName()); - - private URL zipUrl; - private String rootPath = ""; - private int numEntries; - private int tableOffset; - private int tableLength; - private HashMap<String, ZipEntry2> entries; - - private static final ByteBuffer byteBuf = ByteBuffer.allocate(250); - private static final CharBuffer charBuf = CharBuffer.allocate(250); - private static final CharsetDecoder utf8Decoder; - - static { - Charset utf8 = Charset.forName("UTF-8"); - utf8Decoder = utf8.newDecoder(); - } - - private static class ZipEntry2 { - String name; - int length; - int offset; - int compSize; - long crc; - boolean deflate; - - @Override - public String toString(){ - return "ZipEntry[name=" + name + - ", length=" + length + - ", compSize=" + compSize + - ", offset=" + offset + "]"; - } - } - - private static int get16(byte[] b, int off) { - return (b[off++] & 0xff) | - ((b[off] & 0xff) << 8); - } - - private static int get32(byte[] b, int off) { - return (b[off++] & 0xff) | - ((b[off++] & 0xff) << 8) | - ((b[off++] & 0xff) << 16) | - ((b[off] & 0xff) << 24); - } - - private static long getu32(byte[] b, int off) throws IOException{ - return (b[off++]&0xff) | - ((b[off++]&0xff) << 8) | - ((b[off++]&0xff) << 16) | - (((long)(b[off]&0xff)) << 24); - } - - private static String getUTF8String(byte[] b, int off, int len) throws CharacterCodingException { - StringBuilder sb = new StringBuilder(); - - int read = 0; - while (read < len){ - // Either read n remaining bytes in b or 250 if n is higher. - int toRead = Math.min(len - read, byteBuf.capacity()); - - boolean endOfInput = toRead < byteBuf.capacity(); - - // read 'toRead' bytes into byteBuf - byteBuf.put(b, off + read, toRead); - - // set limit to position and set position to 0 - // so data can be decoded - byteBuf.flip(); - - // decode data in byteBuf - CoderResult result = utf8Decoder.decode(byteBuf, charBuf, endOfInput); - - // if the result is not an underflow its an error - // that cannot be handled. - // if the error is an underflow and its the end of input - // then the decoder expects more bytes but there are no more => error - if (!result.isUnderflow() || !endOfInput){ - result.throwException(); - } - - // flip the char buf to get the string just decoded - charBuf.flip(); - - // append the decoded data into the StringBuilder - sb.append(charBuf.toString()); - - // clear buffers for next use - byteBuf.clear(); - charBuf.clear(); - - read += toRead; - } - - return sb.toString(); - } - - private InputStream readData(int offset, int length) throws IOException{ - HttpURLConnection conn = (HttpURLConnection) zipUrl.openConnection(); - conn.setDoOutput(false); - conn.setUseCaches(false); - conn.setInstanceFollowRedirects(false); - String range = "-"; - if (offset != Integer.MAX_VALUE){ - range = offset + range; - } - if (length != Integer.MAX_VALUE){ - if (offset != Integer.MAX_VALUE){ - range = range + (offset + length - 1); - }else{ - range = range + length; - } - } - - conn.setRequestProperty("Range", "bytes=" + range); - conn.connect(); - if (conn.getResponseCode() == HttpURLConnection.HTTP_PARTIAL){ - return conn.getInputStream(); - }else if (conn.getResponseCode() == HttpURLConnection.HTTP_OK){ - throw new IOException("Your server does not support HTTP feature Content-Range. Please contact your server administrator."); - }else{ - throw new IOException(conn.getResponseCode() + " " + conn.getResponseMessage()); - } - } - - private int readTableEntry(byte[] table, int offset) throws IOException{ - if (get32(table, offset) != ZipEntry.CENSIG){ - throw new IOException("Central directory error, expected 'PK12'"); - } - - int nameLen = get16(table, offset + ZipEntry.CENNAM); - int extraLen = get16(table, offset + ZipEntry.CENEXT); - int commentLen = get16(table, offset + ZipEntry.CENCOM); - int newOffset = offset + ZipEntry.CENHDR + nameLen + extraLen + commentLen; - - int flags = get16(table, offset + ZipEntry.CENFLG); - if ((flags & 1) == 1){ - // ignore this entry, it uses encryption - return newOffset; - } - - int method = get16(table, offset + ZipEntry.CENHOW); - if (method != ZipEntry.DEFLATED && method != ZipEntry.STORED){ - // ignore this entry, it uses unknown compression method - return newOffset; - } - - String name = getUTF8String(table, offset + ZipEntry.CENHDR, nameLen); - if (name.charAt(name.length()-1) == '/'){ - // ignore this entry, it is directory node - // or it has no name (?) - return newOffset; - } - - ZipEntry2 entry = new ZipEntry2(); - entry.name = name; - entry.deflate = (method == ZipEntry.DEFLATED); - entry.crc = getu32(table, offset + ZipEntry.CENCRC); - entry.length = get32(table, offset + ZipEntry.CENLEN); - entry.compSize = get32(table, offset + ZipEntry.CENSIZ); - entry.offset = get32(table, offset + ZipEntry.CENOFF); - - // we want offset directly into file data .. - // move the offset forward to skip the LOC header - entry.offset += ZipEntry.LOCHDR + nameLen + extraLen; - - entries.put(entry.name, entry); - - return newOffset; - } - - private void fillByteArray(byte[] array, InputStream source) throws IOException{ - int total = 0; - int length = array.length; - while (total < length) { - int read = source.read(array, total, length - total); - if (read < 0) - throw new IOException("Failed to read entire array"); - - total += read; - } - } - - private void readCentralDirectory() throws IOException{ - InputStream in = readData(tableOffset, tableLength); - byte[] header = new byte[tableLength]; - - // Fix for "PK12 bug in town.zip": sometimes - // not entire byte array will be read with InputStream.read() - // (especially for big headers) - fillByteArray(header, in); - -// in.read(header); - in.close(); - - entries = new HashMap<String, ZipEntry2>(numEntries); - int offset = 0; - for (int i = 0; i < numEntries; i++){ - offset = readTableEntry(header, offset); - } - } - - private void readEndHeader() throws IOException{ - -// InputStream in = readData(Integer.MAX_VALUE, ZipEntry.ENDHDR); -// byte[] header = new byte[ZipEntry.ENDHDR]; -// fillByteArray(header, in); -// in.close(); -// -// if (get32(header, 0) != ZipEntry.ENDSIG){ -// throw new IOException("End header error, expected 'PK56'"); -// } - - // Fix for "PK56 bug in town.zip": - // If there's a zip comment inside the end header, - // PK56 won't appear in the -22 position relative to the end of the - // file! - // In that case, we have to search for it. - // Increase search space to 200 bytes - - InputStream in = readData(Integer.MAX_VALUE, 200); - byte[] header = new byte[200]; - fillByteArray(header, in); - in.close(); - - int offset = -1; - for (int i = 200 - 22; i >= 0; i--){ - if (header[i] == (byte) (ZipEntry.ENDSIG & 0xff) - && get32(header, i) == ZipEntry.ENDSIG){ - // found location - offset = i; - break; - } - } - if (offset == -1) - throw new IOException("Cannot find Zip End Header in file!"); - - numEntries = get16(header, offset + ZipEntry.ENDTOT); - tableLength = get32(header, offset + ZipEntry.ENDSIZ); - tableOffset = get32(header, offset + ZipEntry.ENDOFF); - } - - public void load(URL url) throws IOException { - if (!url.getProtocol().equals("http")) - throw new UnsupportedOperationException(); - - zipUrl = url; - readEndHeader(); - readCentralDirectory(); - } - - private InputStream openStream(ZipEntry2 entry) throws IOException{ - InputStream in = readData(entry.offset, entry.compSize); - if (entry.deflate){ - return new InflaterInputStream(in, new Inflater(true)); - } - return in; - } - - public InputStream openStream(String name) throws IOException{ - ZipEntry2 entry = entries.get(name); - if (entry == null) - throw new RuntimeException("Entry not found: "+name); - - return openStream(entry); - } - - public void setRootPath(String path){ - if (!rootPath.equals(path)){ - rootPath = path; - try { - load(new URL(path)); - } catch (IOException ex) { - logger.log(Level.WARNING, "Failed to set root path "+path, ex); - } - } - } - - public AssetInfo locate(AssetManager manager, AssetKey key){ - final ZipEntry2 entry = entries.get(key.getName()); - if (entry == null) - return null; - - return new AssetInfo(manager, key){ - @Override - public InputStream openStream() { - try { - return HttpZipLocator.this.openStream(entry); - } catch (IOException ex) { - logger.log(Level.WARNING, "Error retrieving "+entry.name, ex); - return null; - } - } - }; - } - -} diff --git a/engine/src/core-plugins/com/jme3/scene/plugins/OBJLoader.java b/engine/src/core-plugins/com/jme3/scene/plugins/OBJLoader.java index 3ce7f52..47165f6 100644 --- a/engine/src/core-plugins/com/jme3/scene/plugins/OBJLoader.java +++ b/engine/src/core-plugins/com/jme3/scene/plugins/OBJLoader.java
@@ -454,9 +454,11 @@ if (hasNormals){ normBuf = BufferUtils.createFloatBuffer(vertIndexMap.size() * 3); + m.setBuffer(VertexBuffer.Type.Normal, 3, normBuf); } if (hasTexCoord){ tcBuf = BufferUtils.createFloatBuffer(vertIndexMap.size() * 2); + m.setBuffer(VertexBuffer.Type.TexCoord, 2, tcBuf); } IndexBuffer indexBuf = null; @@ -517,9 +519,7 @@ } m.setBuffer(VertexBuffer.Type.Position, 3, posBuf); - m.setBuffer(VertexBuffer.Type.Normal, 3, normBuf); - m.setBuffer(VertexBuffer.Type.TexCoord, 2, tcBuf); - // index buffer was set on creation + // index buffer and others were set on creation m.setStatic(); m.updateBound(); diff --git a/engine/src/core/com/jme3/app/Application.java b/engine/src/core/com/jme3/app/Application.java index 517ec61..5100725 100644 --- a/engine/src/core/com/jme3/app/Application.java +++ b/engine/src/core/com/jme3/app/Application.java
@@ -290,6 +290,10 @@ private void initStateManager(){ stateManager = new AppStateManager(this); + + // Always register a ResetStatsState to make sure + // that the stats are cleared every frame + stateManager.attach(new ResetStatsState()); } /** @@ -580,6 +584,17 @@ } task.invoke(); } while (((task = taskQueue.poll()) != null)); + + /* I think the above is really just doing this: + AppTask<?> task; + while( (task = taskQueue.poll()) != null ) { + if (!task.isCancelled()) { + task.invoke(); + } + } + //...but it's hard to say for sure. It's so twisted + //up that I don't trust my eyes. -pspeed + */ if (speed == 0 || paused) return; diff --git a/engine/src/core/com/jme3/app/ResetStatsState.java b/engine/src/core/com/jme3/app/ResetStatsState.java new file mode 100644 index 0000000..03f3013 --- /dev/null +++ b/engine/src/core/com/jme3/app/ResetStatsState.java
@@ -0,0 +1,60 @@ +/* + * Copyright (c) 2009-2012 jMonkeyEngine + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are + * met: + * + * * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * + * * Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * * Neither the name of 'jMonkeyEngine' nor the names of its contributors + * may be used to endorse or promote products derived from this software + * without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED + * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR + * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR + * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, + * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, + * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR + * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF + * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING + * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ +package com.jme3.app; + +import com.jme3.app.state.AbstractAppState; +import com.jme3.renderer.RenderManager; + + +/** + * Resets (clearFrame()) the render's stats object every frame + * during AppState.render(). This state is registered once + * with Application to ensure that the stats are cleared once + * a frame. Using this makes sure that any Appliction based + * application that properly runs its state manager will have + * stats reset no matter how many views it has or if it even + * has views. + * + * @author Paul Speed + */ +public class ResetStatsState extends AbstractAppState { + + public ResetStatsState() { + } + + @Override + public void render(RenderManager rm) { + super.render(rm); + rm.getRenderer().getStatistics().clearFrame(); + } + +} diff --git a/engine/src/core/com/jme3/app/SimpleApplication.java b/engine/src/core/com/jme3/app/SimpleApplication.java index c79ce83..aa8e749 100644 --- a/engine/src/core/com/jme3/app/SimpleApplication.java +++ b/engine/src/core/com/jme3/app/SimpleApplication.java
@@ -241,13 +241,6 @@ rootNode.updateGeometricState(); guiNode.updateGeometricState(); - // Moving this here to make sure it is always done. - // Now the sets are cleared every frame (guaranteed) - // and more than one viewer can access the data. This - // used to be cleared by StatsView but then only StatsView - // could get accurate counts. - renderer.getStatistics().clearFrame(); - // render states stateManager.render(renderManager); renderManager.render(tpf, context.isRenderable());
diff --git a/engine/src/core/com/jme3/app/StatsView.java b/engine/src/core/com/jme3/app/StatsView.java index 49eeb13..f2b2ba5 100644 --- a/engine/src/core/com/jme3/app/StatsView.java +++ b/engine/src/core/com/jme3/app/StatsView.java
@@ -105,7 +105,7 @@ labels[i].setText(stringBuilder); } - // Moved to SimpleApplication to make sure it is + // Moved to ResetStatsState to make sure it is // done even if there is no StatsView or the StatsView // is disable. //statistics.clearFrame();
diff --git a/engine/src/core/com/jme3/app/state/AppStateManager.java b/engine/src/core/com/jme3/app/state/AppStateManager.java index 81228af..92a00f5 100644 --- a/engine/src/core/com/jme3/app/state/AppStateManager.java +++ b/engine/src/core/com/jme3/app/state/AppStateManager.java
@@ -202,6 +202,9 @@ protected void initializePending(){ AppState[] array = getInitializing(); + if (array.length == 0) + return; + synchronized( states ) { // Move the states that will be initialized // into the active array. In all but one case the @@ -219,6 +222,9 @@ protected void terminatePending(){ AppState[] array = getTerminating(); + if (array.length == 0) + return; + for (AppState state : array) { state.cleanup(); } diff --git a/engine/src/core/com/jme3/input/InputManager.java b/engine/src/core/com/jme3/input/InputManager.java index 23f2988..a0c8788 100644 --- a/engine/src/core/com/jme3/input/InputManager.java +++ b/engine/src/core/com/jme3/input/InputManager.java
@@ -1,881 +1,892 @@ -/* - * Copyright (c) 2009-2012 jMonkeyEngine - * All rights reserved. - * - * Redistribution and use in source and binary forms, with or without - * modification, are permitted provided that the following conditions are - * met: - * - * * Redistributions of source code must retain the above copyright - * notice, this list of conditions and the following disclaimer. - * - * * Redistributions in binary form must reproduce the above copyright - * notice, this list of conditions and the following disclaimer in the - * documentation and/or other materials provided with the distribution. - * - * * Neither the name of 'jMonkeyEngine' nor the names of its contributors - * may be used to endorse or promote products derived from this software - * without specific prior written permission. - * - * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS - * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED - * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR - * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR - * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, - * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, - * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR - * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF - * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING - * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS - * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. - */ -package com.jme3.input; - -import com.jme3.app.Application; -import com.jme3.input.controls.*; -import com.jme3.input.event.*; -import com.jme3.math.FastMath; -import com.jme3.math.Vector2f; -import com.jme3.util.IntMap; -import com.jme3.util.IntMap.Entry; -import java.util.ArrayList; -import java.util.HashMap; -import java.util.logging.Level; -import java.util.logging.Logger; - -/** - * The <code>InputManager</code> is responsible for converting input events - * received from the Key, Mouse and Joy Input implementations into an - * abstract, input device independent representation that user code can use. - * <p> - * By default an <code>InputManager</code> is included with every Application instance for use - * in user code to query input, unless the Application is created as headless - * or with input explicitly disabled. - * <p> - * The input manager has two concepts, a {@link Trigger} and a mapping. - * A trigger represents a specific input trigger, such as a key button, - * or a mouse axis. A mapping represents a link onto one or several triggers, - * when the appropriate trigger is activated (e.g. a key is pressed), the - * mapping will be invoked. Any listeners registered to receive an event - * from the mapping will have an event raised. - * <p> - * There are two types of events that {@link InputListener input listeners} - * can receive, one is {@link ActionListener#onAction(java.lang.String, boolean, float) action} - * events and another is {@link AnalogListener#onAnalog(java.lang.String, float, float) analog} - * events. - * <p> - * <code>onAction</code> events are raised when the specific input - * activates or deactivates. For a digital input such as key press, the <code>onAction()</code> - * event will be raised with the <code>isPressed</code> argument equal to true, - * when the key is released, <code>onAction</code> is called again but this time - * with the <code>isPressed</code> argument set to false. - * For analog inputs, the <code>onAction</code> method will be called any time - * the input is non-zero, however an exception to this is for joystick axis inputs, - * which are only called when the input is above the {@link InputManager#setAxisDeadZone(float) dead zone}. - * <p> - * <code>onAnalog</code> events are raised every frame while the input is activated. - * For digital inputs, every frame that the input is active will cause the - * <code>onAnalog</code> method to be called, the argument <code>value</code> - * argument will equal to the frame's time per frame (TPF) value but only - * for digital inputs. For analog inputs however, the <code>value</code> argument - * will equal the actual analog value. - */ -public class InputManager implements RawInputListener { - - private static final Logger logger = Logger.getLogger(InputManager.class.getName()); - private final KeyInput keys; - private final MouseInput mouse; - private final JoyInput joystick; - private final TouchInput touch; - private float frameTPF; - private long lastLastUpdateTime = 0; - private long lastUpdateTime = 0; - private long frameDelta = 0; - private long firstTime = 0; - private boolean eventsPermitted = false; - private boolean mouseVisible = true; - private boolean safeMode = false; - private float axisDeadZone = 0.05f; - private Vector2f cursorPos = new Vector2f(); - private Joystick[] joysticks; - private final IntMap<ArrayList<Mapping>> bindings = new IntMap<ArrayList<Mapping>>(); - private final HashMap<String, Mapping> mappings = new HashMap<String, Mapping>(); - private final IntMap<Long> pressedButtons = new IntMap<Long>(); - private final IntMap<Float> axisValues = new IntMap<Float>(); - private ArrayList<RawInputListener> rawListeners = new ArrayList<RawInputListener>(); - private RawInputListener[] rawListenerArray = null; - private ArrayList<InputEvent> inputQueue = new ArrayList<InputEvent>(); - - private static class Mapping { - - private final String name; - private final ArrayList<Integer> triggers = new ArrayList<Integer>(); - private final ArrayList<InputListener> listeners = new ArrayList<InputListener>(); - - public Mapping(String name) { - this.name = name; - } - } - - /** - * Initializes the InputManager. - * - * <p>This should only be called internally in {@link Application}. - * - * @param mouse - * @param keys - * @param joystick - * @param touch - * @throws IllegalArgumentException If either mouseInput or keyInput are null. - */ - public InputManager(MouseInput mouse, KeyInput keys, JoyInput joystick, TouchInput touch) { - if (keys == null || mouse == null) { - throw new NullPointerException("Mouse or keyboard cannot be null"); - } - - this.keys = keys; - this.mouse = mouse; - this.joystick = joystick; - this.touch = touch; - - keys.setInputListener(this); - mouse.setInputListener(this); - if (joystick != null) { - joystick.setInputListener(this); - joysticks = joystick.loadJoysticks(this); - } - if (touch != null) { - touch.setInputListener(this); - } - - firstTime = keys.getInputTimeNanos(); - } - - private void invokeActions(int hash, boolean pressed) { - ArrayList<Mapping> maps = bindings.get(hash); - if (maps == null) { - return; - } - - int size = maps.size(); - for (int i = size - 1; i >= 0; i--) { - Mapping mapping = maps.get(i); - ArrayList<InputListener> listeners = mapping.listeners; - int listenerSize = listeners.size(); - for (int j = listenerSize - 1; j >= 0; j--) { - InputListener listener = listeners.get(j); - if (listener instanceof ActionListener) { - ((ActionListener) listener).onAction(mapping.name, pressed, frameTPF); - } - } - } - } - - private float computeAnalogValue(long timeDelta) { - if (safeMode || frameDelta == 0) { - return 1f; - } else { - return FastMath.clamp((float) timeDelta / (float) frameDelta, 0, 1); - } - } - - private void invokeTimedActions(int hash, long time, boolean pressed) { - if (!bindings.containsKey(hash)) { - return; - } - - if (pressed) { - pressedButtons.put(hash, time); - } else { - Long pressTimeObj = pressedButtons.remove(hash); - if (pressTimeObj == null) { - return; // under certain circumstances it can be null, ignore - } // the event then. - - long pressTime = pressTimeObj; - long lastUpdate = lastLastUpdateTime; - long releaseTime = time; - long timeDelta = releaseTime - Math.max(pressTime, lastUpdate); - - if (timeDelta > 0) { - invokeAnalogs(hash, computeAnalogValue(timeDelta), false); - } - } - } - - private void invokeUpdateActions() { - for (Entry<Long> pressedButton : pressedButtons) { - int hash = pressedButton.getKey(); - - long pressTime = pressedButton.getValue(); - long timeDelta = lastUpdateTime - Math.max(lastLastUpdateTime, pressTime); - - if (timeDelta > 0) { - invokeAnalogs(hash, computeAnalogValue(timeDelta), false); - } - } - - for (Entry<Float> axisValue : axisValues) { - int hash = axisValue.getKey(); - float value = axisValue.getValue(); - invokeAnalogs(hash, value * frameTPF, true); - } - } - - private void invokeAnalogs(int hash, float value, boolean isAxis) { - ArrayList<Mapping> maps = bindings.get(hash); - if (maps == null) { - return; - } - - if (!isAxis) { - value *= frameTPF; - } - - int size = maps.size(); - for (int i = size - 1; i >= 0; i--) { - Mapping mapping = maps.get(i); - ArrayList<InputListener> listeners = mapping.listeners; - int listenerSize = listeners.size(); - for (int j = listenerSize - 1; j >= 0; j--) { - InputListener listener = listeners.get(j); - if (listener instanceof AnalogListener) { - // NOTE: multiply by TPF for any button bindings - ((AnalogListener) listener).onAnalog(mapping.name, value, frameTPF); - } - } - } - } - - private void invokeAnalogsAndActions(int hash, float value, boolean applyTpf) { - if (value < axisDeadZone) { - invokeAnalogs(hash, value, !applyTpf); - return; - } - - ArrayList<Mapping> maps = bindings.get(hash); - if (maps == null) { - return; - } - - boolean valueChanged = !axisValues.containsKey(hash); - if (applyTpf) { - value *= frameTPF; - } - - int size = maps.size(); - for (int i = size - 1; i >= 0; i--) { - Mapping mapping = maps.get(i); - ArrayList<InputListener> listeners = mapping.listeners; - int listenerSize = listeners.size(); - for (int j = listenerSize - 1; j >= 0; j--) { - InputListener listener = listeners.get(j); - - if (listener instanceof ActionListener && valueChanged) { - ((ActionListener) listener).onAction(mapping.name, true, frameTPF); - } - - if (listener instanceof AnalogListener) { - ((AnalogListener) listener).onAnalog(mapping.name, value, frameTPF); - } - - } - } - } - - /** - * Callback from RawInputListener. Do not use. - */ - public void beginInput() { - } - - /** - * Callback from RawInputListener. Do not use. - */ - public void endInput() { - } - - private void onJoyAxisEventQueued(JoyAxisEvent evt) { -// for (int i = 0; i < rawListeners.size(); i++){ -// rawListeners.get(i).onJoyAxisEvent(evt); -// } - - int joyId = evt.getJoyIndex(); - int axis = evt.getAxisIndex(); - float value = evt.getValue(); - if (value < axisDeadZone && value > -axisDeadZone) { - int hash1 = JoyAxisTrigger.joyAxisHash(joyId, axis, true); - int hash2 = JoyAxisTrigger.joyAxisHash(joyId, axis, false); - - Float val1 = axisValues.get(hash1); - Float val2 = axisValues.get(hash2); - - if (val1 != null && val1.floatValue() > axisDeadZone) { - invokeActions(hash1, false); - } - if (val2 != null && val2.floatValue() > axisDeadZone) { - invokeActions(hash2, false); - } - - axisValues.remove(hash1); - axisValues.remove(hash2); - - } else if (value < 0) { - int hash = JoyAxisTrigger.joyAxisHash(joyId, axis, true); - int otherHash = JoyAxisTrigger.joyAxisHash(joyId, axis, false); - invokeAnalogsAndActions(hash, -value, true); - axisValues.put(hash, -value); - axisValues.remove(otherHash); - } else { - int hash = JoyAxisTrigger.joyAxisHash(joyId, axis, false); - int otherHash = JoyAxisTrigger.joyAxisHash(joyId, axis, true); - invokeAnalogsAndActions(hash, value, true); - axisValues.put(hash, value); - axisValues.remove(otherHash); - } - } - - /** - * Callback from RawInputListener. Do not use. - */ - public void onJoyAxisEvent(JoyAxisEvent evt) { - if (!eventsPermitted) { - throw new UnsupportedOperationException("JoyInput has raised an event at an illegal time."); - } - - inputQueue.add(evt); - } - - private void onJoyButtonEventQueued(JoyButtonEvent evt) { -// for (int i = 0; i < rawListeners.size(); i++){ -// rawListeners.get(i).onJoyButtonEvent(evt); -// } - - int hash = JoyButtonTrigger.joyButtonHash(evt.getJoyIndex(), evt.getButtonIndex()); - invokeActions(hash, evt.isPressed()); - invokeTimedActions(hash, evt.getTime(), evt.isPressed()); - } - - /** - * Callback from RawInputListener. Do not use. - */ - public void onJoyButtonEvent(JoyButtonEvent evt) { - if (!eventsPermitted) { - throw new UnsupportedOperationException("JoyInput has raised an event at an illegal time."); - } - - inputQueue.add(evt); - } - - private void onMouseMotionEventQueued(MouseMotionEvent evt) { -// for (int i = 0; i < rawListeners.size(); i++){ -// rawListeners.get(i).onMouseMotionEvent(evt); -// } - - if (evt.getDX() != 0) { - float val = Math.abs(evt.getDX()) / 1024f; - invokeAnalogsAndActions(MouseAxisTrigger.mouseAxisHash(MouseInput.AXIS_X, evt.getDX() < 0), val, false); - } - if (evt.getDY() != 0) { - float val = Math.abs(evt.getDY()) / 1024f; - invokeAnalogsAndActions(MouseAxisTrigger.mouseAxisHash(MouseInput.AXIS_Y, evt.getDY() < 0), val, false); - } - if (evt.getDeltaWheel() != 0) { - float val = Math.abs(evt.getDeltaWheel()) / 100f; - invokeAnalogsAndActions(MouseAxisTrigger.mouseAxisHash(MouseInput.AXIS_WHEEL, evt.getDeltaWheel() < 0), val, false); - } - } - - /** - * Callback from RawInputListener. Do not use. - */ - public void onMouseMotionEvent(MouseMotionEvent evt) { - if (!eventsPermitted) { - throw new UnsupportedOperationException("MouseInput has raised an event at an illegal time."); - } - - cursorPos.set(evt.getX(), evt.getY()); - inputQueue.add(evt); - } - - private void onMouseButtonEventQueued(MouseButtonEvent evt) { - int hash = MouseButtonTrigger.mouseButtonHash(evt.getButtonIndex()); - invokeActions(hash, evt.isPressed()); - invokeTimedActions(hash, evt.getTime(), evt.isPressed()); - } - - /** - * Callback from RawInputListener. Do not use. - */ - public void onMouseButtonEvent(MouseButtonEvent evt) { - if (!eventsPermitted) { - throw new UnsupportedOperationException("MouseInput has raised an event at an illegal time."); - } - //updating cursor pos on click, so that non android touch events can properly update cursor position. - cursorPos.set(evt.getX(), evt.getY()); - inputQueue.add(evt); - } - - private void onKeyEventQueued(KeyInputEvent evt) { - if (evt.isRepeating()) { - return; // repeat events not used for bindings - } - - int hash = KeyTrigger.keyHash(evt.getKeyCode()); - invokeActions(hash, evt.isPressed()); - invokeTimedActions(hash, evt.getTime(), evt.isPressed()); - } - - /** - * Callback from RawInputListener. Do not use. - */ - public void onKeyEvent(KeyInputEvent evt) { - if (!eventsPermitted) { - throw new UnsupportedOperationException("KeyInput has raised an event at an illegal time."); - } - - inputQueue.add(evt); - } - - /** - * Set the deadzone for joystick axes. - * - * <p>{@link ActionListener#onAction(java.lang.String, boolean, float) } - * events will only be raised if the joystick axis value is greater than - * the <code>deadZone</code>. - * - * @param deadZone the deadzone for joystick axes. - */ - public void setAxisDeadZone(float deadZone) { - this.axisDeadZone = deadZone; - } - - /** - * Returns the deadzone for joystick axes. - * - * @return the deadzone for joystick axes. - */ - public float getAxisDeadZone() { - return axisDeadZone; - } - - /** - * Adds a new listener to receive events on the given mappings. - * - * <p>The given InputListener will be registered to receive events - * on the specified mapping names. When a mapping raises an event, the - * listener will have its appropriate method invoked, either - * {@link ActionListener#onAction(java.lang.String, boolean, float) } - * or {@link AnalogListener#onAnalog(java.lang.String, float, float) } - * depending on which interface the <code>listener</code> implements. - * If the listener implements both interfaces, then it will receive the - * appropriate event for each method. - * - * @param listener The listener to register to receive input events. - * @param mappingNames The mapping names which the listener will receive - * events from. - * - * @see InputManager#removeListener(com.jme3.input.controls.InputListener) - */ - public void addListener(InputListener listener, String... mappingNames) { - for (String mappingName : mappingNames) { - Mapping mapping = mappings.get(mappingName); - if (mapping == null) { - mapping = new Mapping(mappingName); - mappings.put(mappingName, mapping); - } - if (!mapping.listeners.contains(listener)) { - mapping.listeners.add(listener); - } - } - } - - /** - * Removes a listener from receiving events. - * - * <p>This will unregister the listener from any mappings that it - * was previously registered with via - * {@link InputManager#addListener(com.jme3.input.controls.InputListener, java.lang.String[]) }. - * - * @param listener The listener to unregister. - * - * @see InputManager#addListener(com.jme3.input.controls.InputListener, java.lang.String[]) - */ - public void removeListener(InputListener listener) { - for (Mapping mapping : mappings.values()) { - mapping.listeners.remove(listener); - } - } - - /** - * Create a new mapping to the given triggers. - * - * <p> - * The given mapping will be assigned to the given triggers, when - * any of the triggers given raise an event, the listeners - * registered to the mappings will receive appropriate events. - * - * @param mappingName The mapping name to assign. - * @param triggers The triggers to which the mapping is to be registered. - * - * @see InputManager#deleteMapping(java.lang.String) - */ - public void addMapping(String mappingName, Trigger... triggers) { - Mapping mapping = mappings.get(mappingName); - if (mapping == null) { - mapping = new Mapping(mappingName); - mappings.put(mappingName, mapping); - } - - for (Trigger trigger : triggers) { - int hash = trigger.triggerHashCode(); - ArrayList<Mapping> names = bindings.get(hash); - if (names == null) { - names = new ArrayList<Mapping>(); - bindings.put(hash, names); - } - if (!names.contains(mapping)) { - names.add(mapping); - mapping.triggers.add(hash); - } else { - logger.log(Level.WARNING, "Attempted to add mapping \"{0}\" twice to trigger.", mappingName); - } - } - } - - /** - * Returns true if this InputManager has a mapping registered - * for the given mappingName. - * - * @param mappingName The mapping name to check. - * - * @see InputManager#addMapping(java.lang.String, com.jme3.input.controls.Trigger[]) - * @see InputManager#deleteMapping(java.lang.String) - */ - public boolean hasMapping(String mappingName) { - return mappings.containsKey(mappingName); - } - - /** - * Deletes a mapping from receiving trigger events. - * - * <p> - * The given mapping will no longer be assigned to receive trigger - * events. - * - * @param mappingName The mapping name to unregister. - * - * @see InputManager#addMapping(java.lang.String, com.jme3.input.controls.Trigger[]) - */ - public void deleteMapping(String mappingName) { - Mapping mapping = mappings.remove(mappingName); - if (mapping == null) { - throw new IllegalArgumentException("Cannot find mapping: " + mappingName); - } - - ArrayList<Integer> triggers = mapping.triggers; - for (int i = triggers.size() - 1; i >= 0; i--) { - int hash = triggers.get(i); - ArrayList<Mapping> maps = bindings.get(hash); - maps.remove(mapping); - } - } - - /** - * Deletes a specific trigger registered to a mapping. - * - * <p> - * The given mapping will no longer receive events raised by the - * trigger. - * - * @param mappingName The mapping name to cease receiving events from the - * trigger. - * @param trigger The trigger to no longer invoke events on the mapping. - */ - public void deleteTrigger(String mappingName, Trigger trigger) { - Mapping mapping = mappings.get(mappingName); - if (mapping == null) { - throw new IllegalArgumentException("Cannot find mapping: " + mappingName); - } - - ArrayList<Mapping> maps = bindings.get(trigger.triggerHashCode()); - maps.remove(mapping); - - } - - /** - * Clears all the input mappings from this InputManager. - * Consequently, also clears all of the - * InputListeners as well. - */ - public void clearMappings() { - mappings.clear(); - bindings.clear(); - reset(); - } - - /** - * Do not use. - * Called to reset pressed keys or buttons when focus is restored. - */ - public void reset() { - pressedButtons.clear(); - axisValues.clear(); - } - - /** - * Returns whether the mouse cursor is visible or not. - * - * <p>By default the cursor is visible. - * - * @return whether the mouse cursor is visible or not. - * - * @see InputManager#setCursorVisible(boolean) - */ - public boolean isCursorVisible() { - return mouseVisible; - } - - /** - * Set whether the mouse cursor should be visible or not. - * - * @param visible whether the mouse cursor should be visible or not. - */ - public void setCursorVisible(boolean visible) { - if (mouseVisible != visible) { - mouseVisible = visible; - mouse.setCursorVisible(mouseVisible); - } - } - - /** - * Returns the current cursor position. The position is relative to the - * bottom-left of the screen and is in pixels. - * - * @return the current cursor position - */ - public Vector2f getCursorPosition() { - return cursorPos; - } - - /** - * Returns an array of all joysticks installed on the system. - * - * @return an array of all joysticks installed on the system. - */ - public Joystick[] getJoysticks() { - return joysticks; - } - - /** - * Adds a {@link RawInputListener} to receive raw input events. - * - * <p> - * Any raw input listeners registered to this <code>InputManager</code> - * will receive raw input events first, before they get handled - * by the <code>InputManager</code> itself. The listeners are - * each processed in the order they were added, e.g. FIFO. - * <p> - * If a raw input listener has handled the event and does not wish - * other listeners down the list to process the event, it may set the - * {@link InputEvent#setConsumed() consumed flag} to indicate the - * event was consumed and shouldn't be processed any further. - * The listener may do this either at each of the event callbacks - * or at the {@link RawInputListener#endInput() } method. - * - * @param listener A listener to receive raw input events. - * - * @see RawInputListener - */ - public void addRawInputListener(RawInputListener listener) { - rawListeners.add(listener); - rawListenerArray = null; - } - - /** - * Removes a {@link RawInputListener} so that it no longer - * receives raw input events. - * - * @param listener The listener to cease receiving raw input events. - * - * @see InputManager#addRawInputListener(com.jme3.input.RawInputListener) - */ - public void removeRawInputListener(RawInputListener listener) { - rawListeners.remove(listener); - rawListenerArray = null; - } - - /** - * Clears all {@link RawInputListener}s. - * - * @see InputManager#addRawInputListener(com.jme3.input.RawInputListener) - */ - public void clearRawInputListeners() { - rawListeners.clear(); - rawListenerArray = null; - } - - private RawInputListener[] getRawListenerArray() { - if (rawListenerArray == null) - rawListenerArray = rawListeners.toArray(new RawInputListener[rawListeners.size()]); - return rawListenerArray; - } - - /** - * Enable simulation of mouse events. Used for touchscreen input only. - * - * @param value True to enable simulation of mouse events - */ - public void setSimulateMouse(boolean value) { - if (touch != null) { - touch.setSimulateMouse(value); - } - } - - /** - * Enable simulation of keyboard events. Used for touchscreen input only. - * - * @param value True to enable simulation of keyboard events - */ - public void setSimulateKeyboard(boolean value) { - if (touch != null) { - touch.setSimulateKeyboard(value); - } - } - - private void processQueue() { - int queueSize = inputQueue.size(); - RawInputListener[] array = getRawListenerArray(); - - for (RawInputListener listener : array) { - listener.beginInput(); - - for (int j = 0; j < queueSize; j++) { - InputEvent event = inputQueue.get(j); - if (event.isConsumed()) { - continue; - } - - if (event instanceof MouseMotionEvent) { - listener.onMouseMotionEvent((MouseMotionEvent) event); - } else if (event instanceof KeyInputEvent) { - listener.onKeyEvent((KeyInputEvent) event); - } else if (event instanceof MouseButtonEvent) { - listener.onMouseButtonEvent((MouseButtonEvent) event); - } else if (event instanceof JoyAxisEvent) { - listener.onJoyAxisEvent((JoyAxisEvent) event); - } else if (event instanceof JoyButtonEvent) { - listener.onJoyButtonEvent((JoyButtonEvent) event); - } else if (event instanceof TouchEvent) { - listener.onTouchEvent((TouchEvent) event); - } else { - assert false; - } - } - - listener.endInput(); - } - - for (int i = 0; i < queueSize; i++) { - InputEvent event = inputQueue.get(i); - if (event.isConsumed()) { - continue; - } - - if (event instanceof MouseMotionEvent) { - onMouseMotionEventQueued((MouseMotionEvent) event); - } else if (event instanceof KeyInputEvent) { - onKeyEventQueued((KeyInputEvent) event); - } else if (event instanceof MouseButtonEvent) { - onMouseButtonEventQueued((MouseButtonEvent) event); - } else if (event instanceof JoyAxisEvent) { - onJoyAxisEventQueued((JoyAxisEvent) event); - } else if (event instanceof JoyButtonEvent) { - onJoyButtonEventQueued((JoyButtonEvent) event); - } else if (event instanceof TouchEvent) { - onTouchEventQueued((TouchEvent) event); - } else { - assert false; - } - // larynx, 2011.06.10 - flag event as reusable because - // the android input uses a non-allocating ringbuffer which - // needs to know when the event is not anymore in inputQueue - // and therefor can be reused. - event.setConsumed(); - } - - inputQueue.clear(); - } - - /** - * Updates the <code>InputManager</code>. - * This will query current input devices and send - * appropriate events to registered listeners. - * - * @param tpf Time per frame value. - */ - public void update(float tpf) { - frameTPF = tpf; - - // Activate safemode if the TPF value is so small - // that rounding errors are inevitable - safeMode = tpf < 0.015f; - - long currentTime = keys.getInputTimeNanos(); - frameDelta = currentTime - lastUpdateTime; - - eventsPermitted = true; - - keys.update(); - mouse.update(); - if (joystick != null) { - joystick.update(); - } - if (touch != null) { - touch.update(); - } - - eventsPermitted = false; - - processQueue(); - invokeUpdateActions(); - - lastLastUpdateTime = lastUpdateTime; - lastUpdateTime = currentTime; - } - - /** - * Dispatches touch events to touch listeners - * @param evt The touch event to be dispatched to all onTouch listeners - */ - public void onTouchEventQueued(TouchEvent evt) { - ArrayList<Mapping> maps = bindings.get(TouchTrigger.touchHash(evt.getKeyCode())); - if (maps == null) { - return; - } - - int size = maps.size(); - for (int i = size - 1; i >= 0; i--) { - Mapping mapping = maps.get(i); - ArrayList<InputListener> listeners = mapping.listeners; - int listenerSize = listeners.size(); - for (int j = listenerSize - 1; j >= 0; j--) { - InputListener listener = listeners.get(j); - if (listener instanceof TouchListener) { - ((TouchListener) listener).onTouch(mapping.name, evt, frameTPF); - } - } - } - } - - /** - * Callback from RawInputListener. Do not use. - */ - @Override - public void onTouchEvent(TouchEvent evt) { - if (!eventsPermitted) { - throw new UnsupportedOperationException("TouchInput has raised an event at an illegal time."); - } - inputQueue.add(evt); - } -} +/* + * Copyright (c) 2009-2012 jMonkeyEngine + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are + * met: + * + * * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * + * * Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * * Neither the name of 'jMonkeyEngine' nor the names of its contributors + * may be used to endorse or promote products derived from this software + * without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED + * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR + * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR + * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, + * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, + * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR + * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF + * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING + * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ +package com.jme3.input; + +import com.jme3.app.Application; +import com.jme3.input.controls.*; +import com.jme3.input.event.*; +import com.jme3.math.FastMath; +import com.jme3.math.Vector2f; +import com.jme3.util.IntMap; +import com.jme3.util.IntMap.Entry; +import java.util.ArrayList; +import java.util.HashMap; +import java.util.logging.Level; +import java.util.logging.Logger; + +/** + * The <code>InputManager</code> is responsible for converting input events + * received from the Key, Mouse and Joy Input implementations into an + * abstract, input device independent representation that user code can use. + * <p> + * By default an <code>InputManager</code> is included with every Application instance for use + * in user code to query input, unless the Application is created as headless + * or with input explicitly disabled. + * <p> + * The input manager has two concepts, a {@link Trigger} and a mapping. + * A trigger represents a specific input trigger, such as a key button, + * or a mouse axis. A mapping represents a link onto one or several triggers, + * when the appropriate trigger is activated (e.g. a key is pressed), the + * mapping will be invoked. Any listeners registered to receive an event + * from the mapping will have an event raised. + * <p> + * There are two types of events that {@link InputListener input listeners} + * can receive, one is {@link ActionListener#onAction(java.lang.String, boolean, float) action} + * events and another is {@link AnalogListener#onAnalog(java.lang.String, float, float) analog} + * events. + * <p> + * <code>onAction</code> events are raised when the specific input + * activates or deactivates. For a digital input such as key press, the <code>onAction()</code> + * event will be raised with the <code>isPressed</code> argument equal to true, + * when the key is released, <code>onAction</code> is called again but this time + * with the <code>isPressed</code> argument set to false. + * For analog inputs, the <code>onAction</code> method will be called any time + * the input is non-zero, however an exception to this is for joystick axis inputs, + * which are only called when the input is above the {@link InputManager#setAxisDeadZone(float) dead zone}. + * <p> + * <code>onAnalog</code> events are raised every frame while the input is activated. + * For digital inputs, every frame that the input is active will cause the + * <code>onAnalog</code> method to be called, the argument <code>value</code> + * argument will equal to the frame's time per frame (TPF) value but only + * for digital inputs. For analog inputs however, the <code>value</code> argument + * will equal the actual analog value. + */ +public class InputManager implements RawInputListener { + + private static final Logger logger = Logger.getLogger(InputManager.class.getName()); + private final KeyInput keys; + private final MouseInput mouse; + private final JoyInput joystick; + private final TouchInput touch; + private float frameTPF; + private long lastLastUpdateTime = 0; + private long lastUpdateTime = 0; + private long frameDelta = 0; + private long firstTime = 0; + private boolean eventsPermitted = false; + private boolean mouseVisible = true; + private boolean safeMode = false; + private float axisDeadZone = 0.05f; + private Vector2f cursorPos = new Vector2f(); + private Joystick[] joysticks; + private final IntMap<ArrayList<Mapping>> bindings = new IntMap<ArrayList<Mapping>>(); + private final HashMap<String, Mapping> mappings = new HashMap<String, Mapping>(); + private final IntMap<Long> pressedButtons = new IntMap<Long>(); + private final IntMap<Float> axisValues = new IntMap<Float>(); + private ArrayList<RawInputListener> rawListeners = new ArrayList<RawInputListener>(); + private RawInputListener[] rawListenerArray = null; + private ArrayList<InputEvent> inputQueue = new ArrayList<InputEvent>(); + + private static class Mapping { + + private final String name; + private final ArrayList<Integer> triggers = new ArrayList<Integer>(); + private final ArrayList<InputListener> listeners = new ArrayList<InputListener>(); + + public Mapping(String name) { + this.name = name; + } + } + + /** + * Initializes the InputManager. + * + * <p>This should only be called internally in {@link Application}. + * + * @param mouse + * @param keys + * @param joystick + * @param touch + * @throws IllegalArgumentException If either mouseInput or keyInput are null. + */ + public InputManager(MouseInput mouse, KeyInput keys, JoyInput joystick, TouchInput touch) { + if (keys == null || mouse == null) { + throw new NullPointerException("Mouse or keyboard cannot be null"); + } + + this.keys = keys; + this.mouse = mouse; + this.joystick = joystick; + this.touch = touch; + + keys.setInputListener(this); + mouse.setInputListener(this); + if (joystick != null) { + joystick.setInputListener(this); + joysticks = joystick.loadJoysticks(this); + } + if (touch != null) { + touch.setInputListener(this); + } + + firstTime = keys.getInputTimeNanos(); + } + + private void invokeActions(int hash, boolean pressed) { + ArrayList<Mapping> maps = bindings.get(hash); + if (maps == null) { + return; + } + + int size = maps.size(); + for (int i = size - 1; i >= 0; i--) { + Mapping mapping = maps.get(i); + ArrayList<InputListener> listeners = mapping.listeners; + int listenerSize = listeners.size(); + for (int j = listenerSize - 1; j >= 0; j--) { + InputListener listener = listeners.get(j); + if (listener instanceof ActionListener) { + ((ActionListener) listener).onAction(mapping.name, pressed, frameTPF); + } + } + } + } + + private float computeAnalogValue(long timeDelta) { + if (safeMode || frameDelta == 0) { + return 1f; + } else { + return FastMath.clamp((float) timeDelta / (float) frameDelta, 0, 1); + } + } + + private void invokeTimedActions(int hash, long time, boolean pressed) { + if (!bindings.containsKey(hash)) { + return; + } + + if (pressed) { + pressedButtons.put(hash, time); + } else { + Long pressTimeObj = pressedButtons.remove(hash); + if (pressTimeObj == null) { + return; // under certain circumstances it can be null, ignore + } // the event then. + + long pressTime = pressTimeObj; + long lastUpdate = lastLastUpdateTime; + long releaseTime = time; + long timeDelta = releaseTime - Math.max(pressTime, lastUpdate); + + if (timeDelta > 0) { + invokeAnalogs(hash, computeAnalogValue(timeDelta), false); + } + } + } + + private void invokeUpdateActions() { + if (pressedButtons.size() > 0) for (Entry<Long> pressedButton : pressedButtons) { + int hash = pressedButton.getKey(); + + long pressTime = pressedButton.getValue(); + long timeDelta = lastUpdateTime - Math.max(lastLastUpdateTime, pressTime); + + if (timeDelta > 0) { + invokeAnalogs(hash, computeAnalogValue(timeDelta), false); + } + } + + if (axisValues.size() > 0) for (Entry<Float> axisValue : axisValues) { + int hash = axisValue.getKey(); + float value = axisValue.getValue(); + invokeAnalogs(hash, value * frameTPF, true); + } + } + + private void invokeAnalogs(int hash, float value, boolean isAxis) { + ArrayList<Mapping> maps = bindings.get(hash); + if (maps == null) { + return; + } + + if (!isAxis) { + value *= frameTPF; + } + + int size = maps.size(); + for (int i = size - 1; i >= 0; i--) { + Mapping mapping = maps.get(i); + ArrayList<InputListener> listeners = mapping.listeners; + int listenerSize = listeners.size(); + for (int j = listenerSize - 1; j >= 0; j--) { + InputListener listener = listeners.get(j); + if (listener instanceof AnalogListener) { + // NOTE: multiply by TPF for any button bindings + ((AnalogListener) listener).onAnalog(mapping.name, value, frameTPF); + } + } + } + } + + private void invokeAnalogsAndActions(int hash, float value, boolean applyTpf) { + if (value < axisDeadZone) { + invokeAnalogs(hash, value, !applyTpf); + return; + } + + ArrayList<Mapping> maps = bindings.get(hash); + if (maps == null) { + return; + } + + boolean valueChanged = !axisValues.containsKey(hash); + if (applyTpf) { + value *= frameTPF; + } + + int size = maps.size(); + for (int i = size - 1; i >= 0; i--) { + Mapping mapping = maps.get(i); + ArrayList<InputListener> listeners = mapping.listeners; + int listenerSize = listeners.size(); + for (int j = listenerSize - 1; j >= 0; j--) { + InputListener listener = listeners.get(j); + + if (listener instanceof ActionListener && valueChanged) { + ((ActionListener) listener).onAction(mapping.name, true, frameTPF); + } + + if (listener instanceof AnalogListener) { + ((AnalogListener) listener).onAnalog(mapping.name, value, frameTPF); + } + + } + } + } + + /** + * Callback from RawInputListener. Do not use. + */ + public void beginInput() { + } + + /** + * Callback from RawInputListener. Do not use. + */ + public void endInput() { + } + + private void onJoyAxisEventQueued(JoyAxisEvent evt) { +// for (int i = 0; i < rawListeners.size(); i++){ +// rawListeners.get(i).onJoyAxisEvent(evt); +// } + + int joyId = evt.getJoyIndex(); + int axis = evt.getAxisIndex(); + float value = evt.getValue(); + if (value < axisDeadZone && value > -axisDeadZone) { + int hash1 = JoyAxisTrigger.joyAxisHash(joyId, axis, true); + int hash2 = JoyAxisTrigger.joyAxisHash(joyId, axis, false); + + Float val1 = axisValues.get(hash1); + Float val2 = axisValues.get(hash2); + + if (val1 != null && val1.floatValue() > axisDeadZone) { + invokeActions(hash1, false); + } + if (val2 != null && val2.floatValue() > axisDeadZone) { + invokeActions(hash2, false); + } + + axisValues.remove(hash1); + axisValues.remove(hash2); + + } else if (value < 0) { + int hash = JoyAxisTrigger.joyAxisHash(joyId, axis, true); + int otherHash = JoyAxisTrigger.joyAxisHash(joyId, axis, false); + invokeAnalogsAndActions(hash, -value, true); + axisValues.put(hash, -value); + axisValues.remove(otherHash); + } else { + int hash = JoyAxisTrigger.joyAxisHash(joyId, axis, false); + int otherHash = JoyAxisTrigger.joyAxisHash(joyId, axis, true); + invokeAnalogsAndActions(hash, value, true); + axisValues.put(hash, value); + axisValues.remove(otherHash); + } + } + + /** + * Callback from RawInputListener. Do not use. + */ + public void onJoyAxisEvent(JoyAxisEvent evt) { + if (!eventsPermitted) { + throw new UnsupportedOperationException("JoyInput has raised an event at an illegal time."); + } + + inputQueue.add(evt); + } + + private void onJoyButtonEventQueued(JoyButtonEvent evt) { +// for (int i = 0; i < rawListeners.size(); i++){ +// rawListeners.get(i).onJoyButtonEvent(evt); +// } + + int hash = JoyButtonTrigger.joyButtonHash(evt.getJoyIndex(), evt.getButtonIndex()); + invokeActions(hash, evt.isPressed()); + invokeTimedActions(hash, evt.getTime(), evt.isPressed()); + } + + /** + * Callback from RawInputListener. Do not use. + */ + public void onJoyButtonEvent(JoyButtonEvent evt) { + if (!eventsPermitted) { + throw new UnsupportedOperationException("JoyInput has raised an event at an illegal time."); + } + + inputQueue.add(evt); + } + + private void onMouseMotionEventQueued(MouseMotionEvent evt) { +// for (int i = 0; i < rawListeners.size(); i++){ +// rawListeners.get(i).onMouseMotionEvent(evt); +// } + + if (evt.getDX() != 0) { + float val = Math.abs(evt.getDX()) / 1024f; + invokeAnalogsAndActions(MouseAxisTrigger.mouseAxisHash(MouseInput.AXIS_X, evt.getDX() < 0), val, false); + } + if (evt.getDY() != 0) { + float val = Math.abs(evt.getDY()) / 1024f; + invokeAnalogsAndActions(MouseAxisTrigger.mouseAxisHash(MouseInput.AXIS_Y, evt.getDY() < 0), val, false); + } + if (evt.getDeltaWheel() != 0) { + float val = Math.abs(evt.getDeltaWheel()) / 100f; + invokeAnalogsAndActions(MouseAxisTrigger.mouseAxisHash(MouseInput.AXIS_WHEEL, evt.getDeltaWheel() < 0), val, false); + } + } + + /** + * Callback from RawInputListener. Do not use. + */ + public void onMouseMotionEvent(MouseMotionEvent evt) { + if (!eventsPermitted) { + throw new UnsupportedOperationException("MouseInput has raised an event at an illegal time."); + } + + cursorPos.set(evt.getX(), evt.getY()); + inputQueue.add(evt); + } + + private void onMouseButtonEventQueued(MouseButtonEvent evt) { + int hash = MouseButtonTrigger.mouseButtonHash(evt.getButtonIndex()); + invokeActions(hash, evt.isPressed()); + invokeTimedActions(hash, evt.getTime(), evt.isPressed()); + } + + /** + * Callback from RawInputListener. Do not use. + */ + public void onMouseButtonEvent(MouseButtonEvent evt) { + if (!eventsPermitted) { + throw new UnsupportedOperationException("MouseInput has raised an event at an illegal time."); + } + //updating cursor pos on click, so that non android touch events can properly update cursor position. + cursorPos.set(evt.getX(), evt.getY()); + inputQueue.add(evt); + } + + private void onKeyEventQueued(KeyInputEvent evt) { + if (evt.isRepeating()) { + return; // repeat events not used for bindings + } + + int hash = KeyTrigger.keyHash(evt.getKeyCode()); + invokeActions(hash, evt.isPressed()); + invokeTimedActions(hash, evt.getTime(), evt.isPressed()); + } + + /** + * Callback from RawInputListener. Do not use. + */ + public void onKeyEvent(KeyInputEvent evt) { + if (!eventsPermitted) { + throw new UnsupportedOperationException("KeyInput has raised an event at an illegal time."); + } + + inputQueue.add(evt); + } + + /** + * Set the deadzone for joystick axes. + * + * <p>{@link ActionListener#onAction(java.lang.String, boolean, float) } + * events will only be raised if the joystick axis value is greater than + * the <code>deadZone</code>. + * + * @param deadZone the deadzone for joystick axes. + */ + public void setAxisDeadZone(float deadZone) { + this.axisDeadZone = deadZone; + } + + /** + * Returns the deadzone for joystick axes. + * + * @return the deadzone for joystick axes. + */ + public float getAxisDeadZone() { + return axisDeadZone; + } + + /** + * Adds a new listener to receive events on the given mappings. + * + * <p>The given InputListener will be registered to receive events + * on the specified mapping names. When a mapping raises an event, the + * listener will have its appropriate method invoked, either + * {@link ActionListener#onAction(java.lang.String, boolean, float) } + * or {@link AnalogListener#onAnalog(java.lang.String, float, float) } + * depending on which interface the <code>listener</code> implements. + * If the listener implements both interfaces, then it will receive the + * appropriate event for each method. + * + * @param listener The listener to register to receive input events. + * @param mappingNames The mapping names which the listener will receive + * events from. + * + * @see InputManager#removeListener(com.jme3.input.controls.InputListener) + */ + public void addListener(InputListener listener, String... mappingNames) { + for (String mappingName : mappingNames) { + Mapping mapping = mappings.get(mappingName); + if (mapping == null) { + mapping = new Mapping(mappingName); + mappings.put(mappingName, mapping); + } + if (!mapping.listeners.contains(listener)) { + mapping.listeners.add(listener); + } + } + } + + /** + * Removes a listener from receiving events. + * + * <p>This will unregister the listener from any mappings that it + * was previously registered with via + * {@link InputManager#addListener(com.jme3.input.controls.InputListener, java.lang.String[]) }. + * + * @param listener The listener to unregister. + * + * @see InputManager#addListener(com.jme3.input.controls.InputListener, java.lang.String[]) + */ + public void removeListener(InputListener listener) { + for (Mapping mapping : mappings.values()) { + mapping.listeners.remove(listener); + } + } + + /** + * Create a new mapping to the given triggers. + * + * <p> + * The given mapping will be assigned to the given triggers, when + * any of the triggers given raise an event, the listeners + * registered to the mappings will receive appropriate events. + * + * @param mappingName The mapping name to assign. + * @param triggers The triggers to which the mapping is to be registered. + * + * @see InputManager#deleteMapping(java.lang.String) + */ + public void addMapping(String mappingName, Trigger... triggers) { + Mapping mapping = mappings.get(mappingName); + if (mapping == null) { + mapping = new Mapping(mappingName); + mappings.put(mappingName, mapping); + } + + for (Trigger trigger : triggers) { + int hash = trigger.triggerHashCode(); + ArrayList<Mapping> names = bindings.get(hash); + if (names == null) { + names = new ArrayList<Mapping>(); + bindings.put(hash, names); + } + if (!names.contains(mapping)) { + names.add(mapping); + mapping.triggers.add(hash); + } else { + logger.log(Level.WARNING, "Attempted to add mapping \"{0}\" twice to trigger.", mappingName); + } + } + } + + /** + * Returns true if this InputManager has a mapping registered + * for the given mappingName. + * + * @param mappingName The mapping name to check. + * + * @see InputManager#addMapping(java.lang.String, com.jme3.input.controls.Trigger[]) + * @see InputManager#deleteMapping(java.lang.String) + */ + public boolean hasMapping(String mappingName) { + return mappings.containsKey(mappingName); + } + + /** + * Deletes a mapping from receiving trigger events. + * + * <p> + * The given mapping will no longer be assigned to receive trigger + * events. + * + * @param mappingName The mapping name to unregister. + * + * @see InputManager#addMapping(java.lang.String, com.jme3.input.controls.Trigger[]) + */ + public void deleteMapping(String mappingName) { + Mapping mapping = mappings.remove(mappingName); + if (mapping == null) { + throw new IllegalArgumentException("Cannot find mapping: " + mappingName); + } + + ArrayList<Integer> triggers = mapping.triggers; + for (int i = triggers.size() - 1; i >= 0; i--) { + int hash = triggers.get(i); + ArrayList<Mapping> maps = bindings.get(hash); + maps.remove(mapping); + } + } + + /** + * Deletes a specific trigger registered to a mapping. + * + * <p> + * The given mapping will no longer receive events raised by the + * trigger. + * + * @param mappingName The mapping name to cease receiving events from the + * trigger. + * @param trigger The trigger to no longer invoke events on the mapping. + */ + public void deleteTrigger(String mappingName, Trigger trigger) { + Mapping mapping = mappings.get(mappingName); + if (mapping == null) { + throw new IllegalArgumentException("Cannot find mapping: " + mappingName); + } + + ArrayList<Mapping> maps = bindings.get(trigger.triggerHashCode()); + maps.remove(mapping); + + } + + /** + * Clears all the input mappings from this InputManager. + * Consequently, also clears all of the + * InputListeners as well. + */ + public void clearMappings() { + mappings.clear(); + bindings.clear(); + reset(); + } + + /** + * Do not use. + * Called to reset pressed keys or buttons when focus is restored. + */ + public void reset() { + pressedButtons.clear(); + axisValues.clear(); + } + + /** + * Returns whether the mouse cursor is visible or not. + * + * <p>By default the cursor is visible. + * + * @return whether the mouse cursor is visible or not. + * + * @see InputManager#setCursorVisible(boolean) + */ + public boolean isCursorVisible() { + return mouseVisible; + } + + /** + * Set whether the mouse cursor should be visible or not. + * + * @param visible whether the mouse cursor should be visible or not. + */ + public void setCursorVisible(boolean visible) { + if (mouseVisible != visible) { + mouseVisible = visible; + mouse.setCursorVisible(mouseVisible); + } + } + + /** + * Returns the current cursor position. The position is relative to the + * bottom-left of the screen and is in pixels. + * + * @return the current cursor position + */ + public Vector2f getCursorPosition() { + return cursorPos; + } + + /** + * Returns an array of all joysticks installed on the system. + * + * @return an array of all joysticks installed on the system. + */ + public Joystick[] getJoysticks() { + return joysticks; + } + + /** + * Adds a {@link RawInputListener} to receive raw input events. + * + * <p> + * Any raw input listeners registered to this <code>InputManager</code> + * will receive raw input events first, before they get handled + * by the <code>InputManager</code> itself. The listeners are + * each processed in the order they were added, e.g. FIFO. + * <p> + * If a raw input listener has handled the event and does not wish + * other listeners down the list to process the event, it may set the + * {@link InputEvent#setConsumed() consumed flag} to indicate the + * event was consumed and shouldn't be processed any further. + * The listener may do this either at each of the event callbacks + * or at the {@link RawInputListener#endInput() } method. + * + * @param listener A listener to receive raw input events. + * + * @see RawInputListener + */ + public void addRawInputListener(RawInputListener listener) { + rawListeners.add(listener); + rawListenerArray = null; + } + + /** + * Removes a {@link RawInputListener} so that it no longer + * receives raw input events. + * + * @param listener The listener to cease receiving raw input events. + * + * @see InputManager#addRawInputListener(com.jme3.input.RawInputListener) + */ + public void removeRawInputListener(RawInputListener listener) { + rawListeners.remove(listener); + rawListenerArray = null; + } + + /** + * Clears all {@link RawInputListener}s. + * + * @see InputManager#addRawInputListener(com.jme3.input.RawInputListener) + */ + public void clearRawInputListeners() { + rawListeners.clear(); + rawListenerArray = null; + } + + private RawInputListener[] getRawListenerArray() { + if (rawListenerArray == null) + rawListenerArray = rawListeners.toArray(new RawInputListener[rawListeners.size()]); + return rawListenerArray; + } + + /** + * Enable simulation of mouse events. Used for touchscreen input only. + * + * @param value True to enable simulation of mouse events + */ + public void setSimulateMouse(boolean value) { + if (touch != null) { + touch.setSimulateMouse(value); + } + } + /** + * Returns state of simulation of mouse events. Used for touchscreen input only. + * + */ + public boolean getSimulateMouse() { + if (touch != null) { + return touch.getSimulateMouse(); + } else { + return false; + } + } + + /** + * Enable simulation of keyboard events. Used for touchscreen input only. + * + * @param value True to enable simulation of keyboard events + */ + public void setSimulateKeyboard(boolean value) { + if (touch != null) { + touch.setSimulateKeyboard(value); + } + } + + private void processQueue() { + int queueSize = inputQueue.size(); + RawInputListener[] array = getRawListenerArray(); + + for (RawInputListener listener : array) { + listener.beginInput(); + + for (int j = 0; j < queueSize; j++) { + InputEvent event = inputQueue.get(j); + if (event.isConsumed()) { + continue; + } + + if (event instanceof MouseMotionEvent) { + listener.onMouseMotionEvent((MouseMotionEvent) event); + } else if (event instanceof KeyInputEvent) { + listener.onKeyEvent((KeyInputEvent) event); + } else if (event instanceof MouseButtonEvent) { + listener.onMouseButtonEvent((MouseButtonEvent) event); + } else if (event instanceof JoyAxisEvent) { + listener.onJoyAxisEvent((JoyAxisEvent) event); + } else if (event instanceof JoyButtonEvent) { + listener.onJoyButtonEvent((JoyButtonEvent) event); + } else if (event instanceof TouchEvent) { + listener.onTouchEvent((TouchEvent) event); + } else { + assert false; + } + } + + listener.endInput(); + } + + for (int i = 0; i < queueSize; i++) { + InputEvent event = inputQueue.get(i); + if (event.isConsumed()) { + continue; + } + + if (event instanceof MouseMotionEvent) { + onMouseMotionEventQueued((MouseMotionEvent) event); + } else if (event instanceof KeyInputEvent) { + onKeyEventQueued((KeyInputEvent) event); + } else if (event instanceof MouseButtonEvent) { + onMouseButtonEventQueued((MouseButtonEvent) event); + } else if (event instanceof JoyAxisEvent) { + onJoyAxisEventQueued((JoyAxisEvent) event); + } else if (event instanceof JoyButtonEvent) { + onJoyButtonEventQueued((JoyButtonEvent) event); + } else if (event instanceof TouchEvent) { + onTouchEventQueued((TouchEvent) event); + } else { + assert false; + } + // larynx, 2011.06.10 - flag event as reusable because + // the android input uses a non-allocating ringbuffer which + // needs to know when the event is not anymore in inputQueue + // and therefor can be reused. + event.setConsumed(); + } + + inputQueue.clear(); + } + + /** + * Updates the <code>InputManager</code>. + * This will query current input devices and send + * appropriate events to registered listeners. + * + * @param tpf Time per frame value. + */ + public void update(float tpf) { + frameTPF = tpf; + + // Activate safemode if the TPF value is so small + // that rounding errors are inevitable + safeMode = tpf < 0.015f; + + long currentTime = keys.getInputTimeNanos(); + frameDelta = currentTime - lastUpdateTime; + + eventsPermitted = true; + + keys.update(); + mouse.update(); + if (joystick != null) { + joystick.update(); + } + if (touch != null) { + touch.update(); + } + + eventsPermitted = false; + + processQueue(); + invokeUpdateActions(); + + lastLastUpdateTime = lastUpdateTime; + lastUpdateTime = currentTime; + } + + /** + * Dispatches touch events to touch listeners + * @param evt The touch event to be dispatched to all onTouch listeners + */ + public void onTouchEventQueued(TouchEvent evt) { + ArrayList<Mapping> maps = bindings.get(TouchTrigger.touchHash(evt.getKeyCode())); + if (maps == null) { + return; + } + + int size = maps.size(); + for (int i = size - 1; i >= 0; i--) { + Mapping mapping = maps.get(i); + ArrayList<InputListener> listeners = mapping.listeners; + int listenerSize = listeners.size(); + for (int j = listenerSize - 1; j >= 0; j--) { + InputListener listener = listeners.get(j); + if (listener instanceof TouchListener) { + ((TouchListener) listener).onTouch(mapping.name, evt, frameTPF); + } + } + } + } + + /** + * Callback from RawInputListener. Do not use. + */ + @Override + public void onTouchEvent(TouchEvent evt) { + if (!eventsPermitted) { + throw new UnsupportedOperationException("TouchInput has raised an event at an illegal time."); + } + inputQueue.add(evt); + } +} diff --git a/engine/src/core/com/jme3/input/SoftTextDialogInput.java b/engine/src/core/com/jme3/input/SoftTextDialogInput.java new file mode 100644 index 0000000..d166b97 --- /dev/null +++ b/engine/src/core/com/jme3/input/SoftTextDialogInput.java
@@ -0,0 +1,13 @@ +package com.jme3.input; + +import com.jme3.input.controls.SoftTextDialogInputListener; + +public interface SoftTextDialogInput { + + public static int TEXT_ENTRY_DIALOG = 0; + public static int NUMERIC_ENTRY_DIALOG = 1; + public static int NUMERIC_KEYPAD_DIALOG = 2; + + public void requestDialog(int id, String title, String initialValue, SoftTextDialogInputListener listener); + +} diff --git a/engine/src/core/com/jme3/input/TouchInput.java b/engine/src/core/com/jme3/input/TouchInput.java index 2f45b44..e69c4b3 100644 --- a/engine/src/core/com/jme3/input/TouchInput.java +++ b/engine/src/core/com/jme3/input/TouchInput.java
@@ -73,6 +73,12 @@ * @param simulate if mouse events should be generated */ public void setSimulateMouse(boolean simulate); + + /** + * Get if mouse events are generated + * + */ + public boolean getSimulateMouse(); /** * Set if keyboard events should be generated
diff --git a/engine/src/core/com/jme3/input/controls/SoftTextDialogInputListener.java b/engine/src/core/com/jme3/input/controls/SoftTextDialogInputListener.java new file mode 100644 index 0000000..d33844d --- /dev/null +++ b/engine/src/core/com/jme3/input/controls/SoftTextDialogInputListener.java
@@ -0,0 +1,44 @@ +/* + * Copyright (c) 2009-2010 jMonkeyEngine + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are + * met: + * + * * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * + * * Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * * Neither the name of 'jMonkeyEngine' nor the names of its contributors + * may be used to endorse or promote products derived from this software + * without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED + * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR + * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR + * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, + * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, + * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR + * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF + * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING + * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ +package com.jme3.input.controls; + +/** + * + * @author potterec (aka iwgeric) + */ +public interface SoftTextDialogInputListener { + + public static int COMPLETE = 0; + public static int CANCEL = 1; + + public void onSoftText(int action, String text); +} diff --git a/engine/src/core/com/jme3/math/Transform.java b/engine/src/core/com/jme3/math/Transform.java index 7ccd847..3573d8c 100644 --- a/engine/src/core/com/jme3/math/Transform.java +++ b/engine/src/core/com/jme3/math/Transform.java
@@ -1,318 +1,318 @@ -/* - * Copyright (c) 2009-2010 jMonkeyEngine - * All rights reserved. - * - * Redistribution and use in source and binary forms, with or without - * modification, are permitted provided that the following conditions are - * met: - * - * * Redistributions of source code must retain the above copyright - * notice, this list of conditions and the following disclaimer. - * - * * Redistributions in binary form must reproduce the above copyright - * notice, this list of conditions and the following disclaimer in the - * documentation and/or other materials provided with the distribution. - * - * * Neither the name of 'jMonkeyEngine' nor the names of its contributors - * may be used to endorse or promote products derived from this software - * without specific prior written permission. - * - * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS - * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED - * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR - * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR - * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, - * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, - * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR - * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF - * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING - * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS - * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. - */ - -package com.jme3.math; - -import com.jme3.export.*; -import java.io.IOException; - -/** - * Started Date: Jul 16, 2004<br><br> - * Represents a translation, rotation and scale in one object. - * - * @author Jack Lindamood - * @author Joshua Slack - */ -public final class Transform implements Savable, Cloneable, java.io.Serializable { - - static final long serialVersionUID = 1; - - public static final Transform IDENTITY = new Transform(); - - private Quaternion rot = new Quaternion(); - private Vector3f translation = new Vector3f(); - private Vector3f scale = new Vector3f(1,1,1); - - public Transform(Vector3f translation, Quaternion rot){ - this.translation.set(translation); - this.rot.set(rot); - } - - public Transform(Vector3f translation, Quaternion rot, Vector3f scale){ - this(translation, rot); - this.scale.set(scale); - } - - public Transform(Vector3f translation){ - this(translation, Quaternion.IDENTITY); - } - - public Transform(Quaternion rot){ - this(Vector3f.ZERO, rot); - } - - public Transform(){ - this(Vector3f.ZERO, Quaternion.IDENTITY); - } - - /** - * Sets this rotation to the given Quaternion value. - * @param rot The new rotation for this matrix. - * @return this - */ - public Transform setRotation(Quaternion rot) { - this.rot.set(rot); - return this; - } - - /** - * Sets this translation to the given value. - * @param trans The new translation for this matrix. - * @return this - */ - public Transform setTranslation(Vector3f trans) { - this.translation.set(trans); - return this; - } - - /** - * Return the translation vector in this matrix. - * @return translation vector. - */ - public Vector3f getTranslation() { - return translation; - } - - /** - * Sets this scale to the given value. - * @param scale The new scale for this matrix. - * @return this - */ - public Transform setScale(Vector3f scale) { - this.scale.set(scale); - return this; - } - - /** - * Sets this scale to the given value. - * @param scale The new scale for this matrix. - * @return this - */ - public Transform setScale(float scale) { - this.scale.set(scale, scale, scale); - return this; - } - - /** - * Return the scale vector in this matrix. - * @return scale vector. - */ - public Vector3f getScale() { - return scale; - } - - /** - * Stores this translation value into the given vector3f. If trans is null, a new vector3f is created to - * hold the value. The value, once stored, is returned. - * @param trans The store location for this matrix's translation. - * @return The value of this matrix's translation. - */ - public Vector3f getTranslation(Vector3f trans) { - if (trans==null) trans=new Vector3f(); - trans.set(this.translation); - return trans; - } - - /** - * Stores this rotation value into the given Quaternion. If quat is null, a new Quaternion is created to - * hold the value. The value, once stored, is returned. - * @param quat The store location for this matrix's rotation. - * @return The value of this matrix's rotation. - */ - public Quaternion getRotation(Quaternion quat) { - if (quat==null) quat=new Quaternion(); - quat.set(rot); - return quat; - } - - /** - * Return the rotation quaternion in this matrix. - * @return rotation quaternion. - */ - public Quaternion getRotation() { - return rot; - } - - /** - * Stores this scale value into the given vector3f. If scale is null, a new vector3f is created to - * hold the value. The value, once stored, is returned. - * @param scale The store location for this matrix's scale. - * @return The value of this matrix's scale. - */ - public Vector3f getScale(Vector3f scale) { - if (scale==null) scale=new Vector3f(); - scale.set(this.scale); - return scale; - } - - /** - * Sets this matrix to the interpolation between the first matrix and the second by delta amount. - * @param t1 The begining transform. - * @param t2 The ending transform. - * @param delta An amount between 0 and 1 representing how far to interpolate from t1 to t2. - */ - public void interpolateTransforms(Transform t1, Transform t2, float delta) { - this.rot.slerp(t1.rot,t2.rot,delta); - this.translation.interpolate(t1.translation,t2.translation,delta); - this.scale.interpolate(t1.scale,t2.scale,delta); - } - - /** - * Changes the values of this matrix acording to it's parent. Very similar to the concept of Node/Spatial transforms. - * @param parent The parent matrix. - * @return This matrix, after combining. - */ - public Transform combineWithParent(Transform parent) { - scale.multLocal(parent.scale); -// rot.multLocal(parent.rot); - parent.rot.mult(rot, rot); - - // This here, is evil code -// parent -// .rot -// .multLocal(translation) -// .multLocal(parent.scale) -// .addLocal(parent.translation); - - translation.multLocal(parent.scale); - parent - .rot - .multLocal(translation) - .addLocal(parent.translation); - return this; - } - - /** - * Sets this matrix's translation to the given x,y,z values. - * @param x This matrix's new x translation. - * @param y This matrix's new y translation. - * @param z This matrix's new z translation. - * @return this - */ - public Transform setTranslation(float x,float y, float z) { - translation.set(x,y,z); - return this; - } - - /** - * Sets this matrix's scale to the given x,y,z values. - * @param x This matrix's new x scale. - * @param y This matrix's new y scale. - * @param z This matrix's new z scale. - * @return this - */ - public Transform setScale(float x, float y, float z) { - scale.set(x,y,z); - return this; - } - - public Vector3f transformVector(final Vector3f in, Vector3f store){ - if (store == null) - store = new Vector3f(); - - // multiply with scale first, then rotate, finally translate (cf. - // Eberly) - return rot.mult(store.set(in).multLocal(scale), store).addLocal(translation); - } - - public Vector3f transformInverseVector(final Vector3f in, Vector3f store){ - if (store == null) - store = new Vector3f(); - - // The author of this code should look above and take the inverse of that - // But for some reason, they didnt .. -// in.subtract(translation, store).divideLocal(scale); -// rot.inverse().mult(store, store); - - in.subtract(translation, store); - rot.inverse().mult(store, store); - store.divideLocal(scale); - - return store; - } - - /** - * Loads the identity. Equal to translation=1,1,1 scale=0,0,0 rot=0,0,0,1. - */ - public void loadIdentity() { - translation.set(0,0,0); - scale.set(1,1,1); - rot.set(0,0,0,1); - } - - @Override - public String toString(){ - return getClass().getSimpleName() + "[ " + translation.x + ", " + translation.y + ", " + translation.z + "]\n" - + "[ " + rot.x + ", " + rot.y + ", " + rot.z + ", " + rot.w + "]\n" - + "[ " + scale.x + " , " + scale.y + ", " + scale.z + "]"; - } - - /** - * Sets this matrix to be equal to the given matrix. - * @param matrixQuat The matrix to be equal to. - * @return this - */ - public Transform set(Transform matrixQuat) { - this.translation.set(matrixQuat.translation); - this.rot.set(matrixQuat.rot); - this.scale.set(matrixQuat.scale); - return this; - } - - public void write(JmeExporter e) throws IOException { - OutputCapsule capsule = e.getCapsule(this); - capsule.write(rot, "rot", new Quaternion()); - capsule.write(translation, "translation", Vector3f.ZERO); - capsule.write(scale, "scale", Vector3f.UNIT_XYZ); - } - - public void read(JmeImporter e) throws IOException { - InputCapsule capsule = e.getCapsule(this); - - rot = (Quaternion)capsule.readSavable("rot", new Quaternion()); - translation = (Vector3f)capsule.readSavable("translation", Vector3f.ZERO); - scale = (Vector3f)capsule.readSavable("scale", Vector3f.UNIT_XYZ); - } - - @Override - public Transform clone() { - try { - Transform tq = (Transform) super.clone(); - tq.rot = rot.clone(); - tq.scale = scale.clone(); - tq.translation = translation.clone(); - return tq; - } catch (CloneNotSupportedException e) { - throw new AssertionError(); - } - } -} +/* + * Copyright (c) 2009-2010 jMonkeyEngine + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are + * met: + * + * * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * + * * Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * * Neither the name of 'jMonkeyEngine' nor the names of its contributors + * may be used to endorse or promote products derived from this software + * without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED + * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR + * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR + * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, + * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, + * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR + * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF + * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING + * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +package com.jme3.math; + +import com.jme3.export.*; +import java.io.IOException; + +/** + * Started Date: Jul 16, 2004<br><br> + * Represents a translation, rotation and scale in one object. + * + * @author Jack Lindamood + * @author Joshua Slack + */ +public final class Transform implements Savable, Cloneable, java.io.Serializable { + + static final long serialVersionUID = 1; + + public static final Transform IDENTITY = new Transform(); + + private Quaternion rot = new Quaternion(); + private Vector3f translation = new Vector3f(); + private Vector3f scale = new Vector3f(1,1,1); + + public Transform(Vector3f translation, Quaternion rot){ + this.translation.set(translation); + this.rot.set(rot); + } + + public Transform(Vector3f translation, Quaternion rot, Vector3f scale){ + this(translation, rot); + this.scale.set(scale); + } + + public Transform(Vector3f translation){ + this(translation, Quaternion.IDENTITY); + } + + public Transform(Quaternion rot){ + this(Vector3f.ZERO, rot); + } + + public Transform(){ + this(Vector3f.ZERO, Quaternion.IDENTITY); + } + + /** + * Sets this rotation to the given Quaternion value. + * @param rot The new rotation for this matrix. + * @return this + */ + public Transform setRotation(Quaternion rot) { + this.rot.set(rot); + return this; + } + + /** + * Sets this translation to the given value. + * @param trans The new translation for this matrix. + * @return this + */ + public Transform setTranslation(Vector3f trans) { + this.translation.set(trans); + return this; + } + + /** + * Return the translation vector in this matrix. + * @return translation vector. + */ + public Vector3f getTranslation() { + return translation; + } + + /** + * Sets this scale to the given value. + * @param scale The new scale for this matrix. + * @return this + */ + public Transform setScale(Vector3f scale) { + this.scale.set(scale); + return this; + } + + /** + * Sets this scale to the given value. + * @param scale The new scale for this matrix. + * @return this + */ + public Transform setScale(float scale) { + this.scale.set(scale, scale, scale); + return this; + } + + /** + * Return the scale vector in this matrix. + * @return scale vector. + */ + public Vector3f getScale() { + return scale; + } + + /** + * Stores this translation value into the given vector3f. If trans is null, a new vector3f is created to + * hold the value. The value, once stored, is returned. + * @param trans The store location for this matrix's translation. + * @return The value of this matrix's translation. + */ + public Vector3f getTranslation(Vector3f trans) { + if (trans==null) trans=new Vector3f(); + trans.set(this.translation); + return trans; + } + + /** + * Stores this rotation value into the given Quaternion. If quat is null, a new Quaternion is created to + * hold the value. The value, once stored, is returned. + * @param quat The store location for this matrix's rotation. + * @return The value of this matrix's rotation. + */ + public Quaternion getRotation(Quaternion quat) { + if (quat==null) quat=new Quaternion(); + quat.set(rot); + return quat; + } + + /** + * Return the rotation quaternion in this matrix. + * @return rotation quaternion. + */ + public Quaternion getRotation() { + return rot; + } + + /** + * Stores this scale value into the given vector3f. If scale is null, a new vector3f is created to + * hold the value. The value, once stored, is returned. + * @param scale The store location for this matrix's scale. + * @return The value of this matrix's scale. + */ + public Vector3f getScale(Vector3f scale) { + if (scale==null) scale=new Vector3f(); + scale.set(this.scale); + return scale; + } + + /** + * Sets this matrix to the interpolation between the first matrix and the second by delta amount. + * @param t1 The begining transform. + * @param t2 The ending transform. + * @param delta An amount between 0 and 1 representing how far to interpolate from t1 to t2. + */ + public void interpolateTransforms(Transform t1, Transform t2, float delta) { + this.rot.slerp(t1.rot,t2.rot,delta); + this.translation.interpolate(t1.translation,t2.translation,delta); + this.scale.interpolate(t1.scale,t2.scale,delta); + } + + /** + * Changes the values of this matrix acording to it's parent. Very similar to the concept of Node/Spatial transforms. + * @param parent The parent matrix. + * @return This matrix, after combining. + */ + public Transform combineWithParent(Transform parent) { + scale.multLocal(parent.scale); +// rot.multLocal(parent.rot); + parent.rot.mult(rot, rot); + + // This here, is evil code +// parent +// .rot +// .multLocal(translation) +// .multLocal(parent.scale) +// .addLocal(parent.translation); + + translation.multLocal(parent.scale); + parent + .rot + .multLocal(translation) + .addLocal(parent.translation); + return this; + } + + /** + * Sets this matrix's translation to the given x,y,z values. + * @param x This matrix's new x translation. + * @param y This matrix's new y translation. + * @param z This matrix's new z translation. + * @return this + */ + public Transform setTranslation(float x,float y, float z) { + translation.set(x,y,z); + return this; + } + + /** + * Sets this matrix's scale to the given x,y,z values. + * @param x This matrix's new x scale. + * @param y This matrix's new y scale. + * @param z This matrix's new z scale. + * @return this + */ + public Transform setScale(float x, float y, float z) { + scale.set(x,y,z); + return this; + } + + public Vector3f transformVector(final Vector3f in, Vector3f store){ + if (store == null) + store = new Vector3f(); + + // multiply with scale first, then rotate, finally translate (cf. + // Eberly) + return rot.mult(store.set(in).multLocal(scale), store).addLocal(translation); + } + + public Vector3f transformInverseVector(final Vector3f in, Vector3f store){ + if (store == null) + store = new Vector3f(); + + // The author of this code should look above and take the inverse of that + // But for some reason, they didnt .. +// in.subtract(translation, store).divideLocal(scale); +// rot.inverse().mult(store, store); + + in.subtract(translation, store); + rot.inverse().mult(store, store); + store.divideLocal(scale); + + return store; + } + + /** + * Loads the identity. Equal to translation=0,0,0 scale=1,1,1 rot=0,0,0,1. + */ + public void loadIdentity() { + translation.set(0,0,0); + scale.set(1,1,1); + rot.set(0,0,0,1); + } + + @Override + public String toString(){ + return getClass().getSimpleName() + "[ " + translation.x + ", " + translation.y + ", " + translation.z + "]\n" + + "[ " + rot.x + ", " + rot.y + ", " + rot.z + ", " + rot.w + "]\n" + + "[ " + scale.x + " , " + scale.y + ", " + scale.z + "]"; + } + + /** + * Sets this matrix to be equal to the given matrix. + * @param matrixQuat The matrix to be equal to. + * @return this + */ + public Transform set(Transform matrixQuat) { + this.translation.set(matrixQuat.translation); + this.rot.set(matrixQuat.rot); + this.scale.set(matrixQuat.scale); + return this; + } + + public void write(JmeExporter e) throws IOException { + OutputCapsule capsule = e.getCapsule(this); + capsule.write(rot, "rot", new Quaternion()); + capsule.write(translation, "translation", Vector3f.ZERO); + capsule.write(scale, "scale", Vector3f.UNIT_XYZ); + } + + public void read(JmeImporter e) throws IOException { + InputCapsule capsule = e.getCapsule(this); + + rot = (Quaternion)capsule.readSavable("rot", new Quaternion()); + translation = (Vector3f)capsule.readSavable("translation", Vector3f.ZERO); + scale = (Vector3f)capsule.readSavable("scale", Vector3f.UNIT_XYZ); + } + + @Override + public Transform clone() { + try { + Transform tq = (Transform) super.clone(); + tq.rot = rot.clone(); + tq.scale = scale.clone(); + tq.translation = translation.clone(); + return tq; + } catch (CloneNotSupportedException e) { + throw new AssertionError(); + } + } +} diff --git a/engine/src/core/com/jme3/post/Filter.java b/engine/src/core/com/jme3/post/Filter.java index ef9a6ff..12babf5 100644 --- a/engine/src/core/com/jme3/post/Filter.java +++ b/engine/src/core/com/jme3/post/Filter.java
@@ -40,6 +40,7 @@ import com.jme3.renderer.ViewPort; import com.jme3.texture.FrameBuffer; import com.jme3.texture.Image.Format; +import com.jme3.texture.Texture; import com.jme3.texture.Texture2D; import java.io.IOException; import java.util.Collection; @@ -236,7 +237,7 @@ * cleanup this filter * @param r */ - protected final void cleanup(Renderer r) { + protected final void cleanup(Renderer r) { processor = null; if (defaultPass != null) { defaultPass.cleanup(r); @@ -269,8 +270,6 @@ protected void cleanUpFilter(Renderer r) { } - ; - /** * Must return the material used for this filter. * this method is called every frame. @@ -278,6 +277,14 @@ * @return the material used for this filter. */ protected abstract Material getMaterial(); + + /** + * Override if you want to do something special with the depth texture; + * @param depthTexture + */ + protected void setDepthTexture(Texture depthTexture){ + getMaterial().setTexture("DepthTexture", depthTexture); + } /** * Override this method if you want to make a pre pass, before the actual rendering of the frame diff --git a/engine/src/core/com/jme3/post/FilterPostProcessor.java b/engine/src/core/com/jme3/post/FilterPostProcessor.java index 2e48f0f..d97e8e9 100644 --- a/engine/src/core/com/jme3/post/FilterPostProcessor.java +++ b/engine/src/core/com/jme3/post/FilterPostProcessor.java
@@ -77,7 +77,7 @@ private int originalHeight; private int lastFilterIndex = -1; private boolean cameraInit = false; - + /** * Create a FilterProcessor * @param assetManager the assetManager @@ -98,8 +98,7 @@ * @param filter the filter to add */ public void addFilter(Filter filter) { - filters.add(filter); - filter.setProcessor(this); + filters.add(filter); if (isInitialized()) { initFilter(filter, viewPort); @@ -148,14 +147,17 @@ * @param vp */ private void initFilter(Filter filter, ViewPort vp) { - filter.init(assetManager, renderManager, vp, width, height); + filter.setProcessor(this); if (filter.isRequiresDepthTexture()) { - if (!computeDepth && renderFrameBuffer != null) { + if (!computeDepth && renderFrameBuffer != null) { depthTexture = new Texture2D(width, height, Format.Depth24); renderFrameBuffer.setDepthTexture(depthTexture); } computeDepth = true; - filter.getMaterial().setTexture("DepthTexture", depthTexture); + filter.init(assetManager, renderManager, vp, width, height); + filter.setDepthTexture(depthTexture); + } else { + filter.init(assetManager, renderManager, vp, width, height); } } @@ -281,9 +283,9 @@ } else if (renderFrameBufferMS != null) { sceneBuffer = renderFrameBufferMS; } - renderFilterChain(renderer, sceneBuffer); + renderFilterChain(renderer, sceneBuffer); renderer.setFrameBuffer(outputBuffer); - + //viewport can be null if no filters are enabled if (viewPort != null) { renderManager.setCamera(viewPort.getCamera(), false); @@ -356,8 +358,11 @@ //reseting the viewport camera viewport to its initial value viewPort.getCamera().resize(originalWidth, originalHeight, true); viewPort.getCamera().setViewPort(left, right, bottom, top); - viewPort.setOutputFrameBuffer(outputBuffer); + viewPort.setOutputFrameBuffer(outputBuffer); viewPort = null; + for (Filter filter : filters) { + filter.cleanup(renderer); + } } } @@ -484,7 +489,7 @@ * For internal use only<br> * returns the depth texture of the scene * @return - */ + */ public Texture2D getDepthTexture() { return depthTexture; } diff --git a/engine/src/core/com/jme3/renderer/RenderManager.java b/engine/src/core/com/jme3/renderer/RenderManager.java index 1d58d22..a0f5fdf 100644 --- a/engine/src/core/com/jme3/renderer/RenderManager.java +++ b/engine/src/core/com/jme3/renderer/RenderManager.java
@@ -1,1170 +1,1178 @@ -/* - * Copyright (c) 2009-2012 jMonkeyEngine - * All rights reserved. - * - * Redistribution and use in source and binary forms, with or without - * modification, are permitted provided that the following conditions are - * met: - * - * * Redistributions of source code must retain the above copyright - * notice, this list of conditions and the following disclaimer. - * - * * Redistributions in binary form must reproduce the above copyright - * notice, this list of conditions and the following disclaimer in the - * documentation and/or other materials provided with the distribution. - * - * * Neither the name of 'jMonkeyEngine' nor the names of its contributors - * may be used to endorse or promote products derived from this software - * without specific prior written permission. - * - * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS - * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED - * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR - * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR - * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, - * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, - * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR - * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF - * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING - * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS - * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. - */ -package com.jme3.renderer; - -import com.jme3.material.Material; -import com.jme3.material.MaterialDef; -import com.jme3.material.RenderState; -import com.jme3.material.Technique; -import com.jme3.math.*; -import com.jme3.post.SceneProcessor; -import com.jme3.renderer.queue.GeometryList; -import com.jme3.renderer.queue.RenderQueue; -import com.jme3.renderer.queue.RenderQueue.Bucket; -import com.jme3.renderer.queue.RenderQueue.ShadowMode; -import com.jme3.scene.*; -import com.jme3.shader.Uniform; -import com.jme3.shader.UniformBinding; -import com.jme3.shader.VarType; -import com.jme3.system.NullRenderer; -import com.jme3.system.Timer; -import com.jme3.util.IntMap.Entry; -import com.jme3.util.TempVars; -import java.util.ArrayList; -import java.util.Collections; -import java.util.List; -import java.util.logging.Logger; - -/** - * <code>RenderManager</code> is a high-level rendering interface that is - * above the Renderer implementation. RenderManager takes care - * of rendering the scene graphs attached to each viewport and - * handling SceneProcessors. - * - * @see SceneProcessor - * @see ViewPort - * @see Spatial - */ -public class RenderManager { - - private static final Logger logger = Logger.getLogger(RenderManager.class.getName()); - - private Renderer renderer; - private Timer timer; - private ArrayList<ViewPort> preViewPorts = new ArrayList<ViewPort>(); - private ArrayList<ViewPort> viewPorts = new ArrayList<ViewPort>(); - private ArrayList<ViewPort> postViewPorts = new ArrayList<ViewPort>(); - private Camera prevCam = null; - private Material forcedMaterial = null; - private String forcedTechnique = null; - private RenderState forcedRenderState = null; - private boolean shader; - private int viewX, viewY, viewWidth, viewHeight; - private float near, far; - private Matrix4f orthoMatrix = new Matrix4f(); - private Matrix4f viewMatrix = new Matrix4f(); - private Matrix4f projMatrix = new Matrix4f(); - private Matrix4f viewProjMatrix = new Matrix4f(); - private Matrix4f worldMatrix = new Matrix4f(); - private Vector3f camUp = new Vector3f(), - camLeft = new Vector3f(), - camDir = new Vector3f(), - camLoc = new Vector3f(); - //temp technique - private String tmpTech; - private boolean handleTranlucentBucket = true; - - /** - * Create a high-level rendering interface over the - * low-level rendering interface. - * @param renderer - */ - public RenderManager(Renderer renderer) { - this.renderer = renderer; - //this.shader = renderer.getCaps().contains(Caps.GLSL100); - } - - /** - * Returns the pre ViewPort with the given name. - * - * @param viewName The name of the pre ViewPort to look up - * @return The ViewPort, or null if not found. - * - * @see #createPreView(java.lang.String, com.jme3.renderer.Camera) - */ - public ViewPort getPreView(String viewName) { - for (int i = 0; i < preViewPorts.size(); i++) { - if (preViewPorts.get(i).getName().equals(viewName)) { - return preViewPorts.get(i); - } - } - return null; - } - - /** - * Removes the specified pre ViewPort. - * - * @param view The pre ViewPort to remove - * @return True if the ViewPort was removed successfully. - * - * @see #createPreView(java.lang.String, com.jme3.renderer.Camera) - */ - public boolean removePreView(ViewPort view) { - return preViewPorts.remove(view); - } - - /** - * Returns the main ViewPort with the given name. - * - * @param viewName The name of the main ViewPort to look up - * @return The ViewPort, or null if not found. - * - * @see #createMainView(java.lang.String, com.jme3.renderer.Camera) - */ - public ViewPort getMainView(String viewName) { - for (int i = 0; i < viewPorts.size(); i++) { - if (viewPorts.get(i).getName().equals(viewName)) { - return viewPorts.get(i); - } - } - return null; - } - - /** - * Removes the main ViewPort with the specified name. - * - * @param viewName The main ViewPort name to remove - * @return True if the ViewPort was removed successfully. - * - * @see #createMainView(java.lang.String, com.jme3.renderer.Camera) - */ - public boolean removeMainView(String viewName) { - for (int i = 0; i < viewPorts.size(); i++) { - if (viewPorts.get(i).getName().equals(viewName)) { - viewPorts.remove(i); - return true; - } - } - return false; - } - - /** - * Removes the specified main ViewPort. - * - * @param view The main ViewPort to remove - * @return True if the ViewPort was removed successfully. - * - * @see #createMainView(java.lang.String, com.jme3.renderer.Camera) - */ - public boolean removeMainView(ViewPort view) { - return viewPorts.remove(view); - } - - /** - * Returns the post ViewPort with the given name. - * - * @param viewName The name of the post ViewPort to look up - * @return The ViewPort, or null if not found. - * - * @see #createPostView(java.lang.String, com.jme3.renderer.Camera) - */ - public ViewPort getPostView(String viewName) { - for (int i = 0; i < postViewPorts.size(); i++) { - if (postViewPorts.get(i).getName().equals(viewName)) { - return postViewPorts.get(i); - } - } - return null; - } - - /** - * Removes the post ViewPort with the specified name. - * - * @param viewName The post ViewPort name to remove - * @return True if the ViewPort was removed successfully. - * - * @see #createPostView(java.lang.String, com.jme3.renderer.Camera) - */ - public boolean removePostView(String viewName) { - for (int i = 0; i < postViewPorts.size(); i++) { - if (postViewPorts.get(i).getName().equals(viewName)) { - postViewPorts.remove(i); - - return true; - } - } - return false; - } - - /** - * Removes the specified post ViewPort. - * - * @param view The post ViewPort to remove - * @return True if the ViewPort was removed successfully. - * - * @see #createPostView(java.lang.String, com.jme3.renderer.Camera) - */ - public boolean removePostView(ViewPort view) { - return postViewPorts.remove(view); - } - - /** - * Returns a read-only list of all pre ViewPorts - * @return a read-only list of all pre ViewPorts - * @see #createPreView(java.lang.String, com.jme3.renderer.Camera) - */ - public List<ViewPort> getPreViews() { - return Collections.unmodifiableList(preViewPorts); - } - - /** - * Returns a read-only list of all main ViewPorts - * @return a read-only list of all main ViewPorts - * @see #createMainView(java.lang.String, com.jme3.renderer.Camera) - */ - public List<ViewPort> getMainViews() { - return Collections.unmodifiableList(viewPorts); - } - - /** - * Returns a read-only list of all post ViewPorts - * @return a read-only list of all post ViewPorts - * @see #createPostView(java.lang.String, com.jme3.renderer.Camera) - */ - public List<ViewPort> getPostViews() { - return Collections.unmodifiableList(postViewPorts); - } - - /** - * Creates a new pre ViewPort, to display the given camera's content. - * <p> - * The view will be processed before the main and post viewports. - */ - public ViewPort createPreView(String viewName, Camera cam) { - ViewPort vp = new ViewPort(viewName, cam); - preViewPorts.add(vp); - return vp; - } - - /** - * Creates a new main ViewPort, to display the given camera's content. - * <p> - * The view will be processed before the post viewports but after - * the pre viewports. - */ - public ViewPort createMainView(String viewName, Camera cam) { - ViewPort vp = new ViewPort(viewName, cam); - viewPorts.add(vp); - return vp; - } - - /** - * Creates a new post ViewPort, to display the given camera's content. - * <p> - * The view will be processed after the pre and main viewports. - */ - public ViewPort createPostView(String viewName, Camera cam) { - ViewPort vp = new ViewPort(viewName, cam); - postViewPorts.add(vp); - return vp; - } - - private void notifyReshape(ViewPort vp, int w, int h) { - List<SceneProcessor> processors = vp.getProcessors(); - for (SceneProcessor proc : processors) { - if (!proc.isInitialized()) { - proc.initialize(this, vp); - } else { - proc.reshape(vp, w, h); - } - } - } - - /** - * Internal use only. - * Updates the resolution of all on-screen cameras to match - * the given width and height. - */ - public void notifyReshape(int w, int h) { - for (ViewPort vp : preViewPorts) { - if (vp.getOutputFrameBuffer() == null) { - Camera cam = vp.getCamera(); - cam.resize(w, h, true); - } - notifyReshape(vp, w, h); - } - for (ViewPort vp : viewPorts) { - if (vp.getOutputFrameBuffer() == null) { - Camera cam = vp.getCamera(); - cam.resize(w, h, true); - } - notifyReshape(vp, w, h); - } - for (ViewPort vp : postViewPorts) { - if (vp.getOutputFrameBuffer() == null) { - Camera cam = vp.getCamera(); - cam.resize(w, h, true); - } - notifyReshape(vp, w, h); - } - } - - /** - * Internal use only. - * Updates the given list of uniforms with {@link UniformBinding uniform bindings} - * based on the current world state. - */ - public void updateUniformBindings(List<Uniform> params) { - // assums worldMatrix is properly set. - TempVars vars = TempVars.get(); - - Matrix4f tempMat4 = vars.tempMat4; - Matrix3f tempMat3 = vars.tempMat3; - Vector2f tempVec2 = vars.vect2d; - Quaternion tempVec4 = vars.quat1; - - for (int i = 0; i < params.size(); i++) { - Uniform u = params.get(i); - switch (u.getBinding()) { - case WorldMatrix: - u.setValue(VarType.Matrix4, worldMatrix); - break; - case ViewMatrix: - u.setValue(VarType.Matrix4, viewMatrix); - break; - case ProjectionMatrix: - u.setValue(VarType.Matrix4, projMatrix); - break; - case ViewProjectionMatrix: - u.setValue(VarType.Matrix4, viewProjMatrix); - break; - case WorldViewMatrix: - tempMat4.set(viewMatrix); - tempMat4.multLocal(worldMatrix); - u.setValue(VarType.Matrix4, tempMat4); - break; - case NormalMatrix: - tempMat4.set(viewMatrix); - tempMat4.multLocal(worldMatrix); - tempMat4.toRotationMatrix(tempMat3); - tempMat3.invertLocal(); - tempMat3.transposeLocal(); - u.setValue(VarType.Matrix3, tempMat3); - break; - case WorldViewProjectionMatrix: - tempMat4.set(viewProjMatrix); - tempMat4.multLocal(worldMatrix); - u.setValue(VarType.Matrix4, tempMat4); - break; - case WorldMatrixInverse: - tempMat4.multLocal(worldMatrix); - tempMat4.invertLocal(); - u.setValue(VarType.Matrix4, tempMat4); - break; - case ViewMatrixInverse: - tempMat4.set(viewMatrix); - tempMat4.invertLocal(); - u.setValue(VarType.Matrix4, tempMat4); - break; - case ProjectionMatrixInverse: - tempMat4.set(projMatrix); - tempMat4.invertLocal(); - u.setValue(VarType.Matrix4, tempMat4); - break; - case ViewProjectionMatrixInverse: - tempMat4.set(viewProjMatrix); - tempMat4.invertLocal(); - u.setValue(VarType.Matrix4, tempMat4); - break; - case WorldViewMatrixInverse: - tempMat4.set(viewMatrix); - tempMat4.multLocal(worldMatrix); - tempMat4.invertLocal(); - u.setValue(VarType.Matrix4, tempMat4); - break; - case NormalMatrixInverse: - tempMat4.set(viewMatrix); - tempMat4.multLocal(worldMatrix); - tempMat4.toRotationMatrix(tempMat3); - tempMat3.invertLocal(); - tempMat3.transposeLocal(); - tempMat3.invertLocal(); - u.setValue(VarType.Matrix3, tempMat3); - break; - case WorldViewProjectionMatrixInverse: - tempMat4.set(viewProjMatrix); - tempMat4.multLocal(worldMatrix); - tempMat4.invertLocal(); - u.setValue(VarType.Matrix4, tempMat4); - break; - case ViewPort: - tempVec4.set(viewX, viewY, viewWidth, viewHeight); - u.setValue(VarType.Vector4, tempVec4); - break; - case Resolution: - tempVec2.set(viewWidth, viewHeight); - u.setValue(VarType.Vector2, tempVec2); - break; - case Aspect: - float aspect = ((float) viewWidth) / viewHeight; - u.setValue(VarType.Float, aspect); - break; - case FrustumNearFar: - tempVec2.set(near, far); - u.setValue(VarType.Vector2, tempVec2); - break; - case CameraPosition: - u.setValue(VarType.Vector3, camLoc); - break; - case CameraDirection: - u.setValue(VarType.Vector3, camDir); - break; - case CameraLeft: - u.setValue(VarType.Vector3, camLeft); - break; - case CameraUp: - u.setValue(VarType.Vector3, camUp); - break; - case Time: - u.setValue(VarType.Float, timer.getTimeInSeconds()); - break; - case Tpf: - u.setValue(VarType.Float, timer.getTimePerFrame()); - break; - case FrameRate: - u.setValue(VarType.Float, timer.getFrameRate()); - break; - } - } - - vars.release(); - } - - /** - * Set the material to use to render all future objects. - * This overrides the material set on the geometry and renders - * with the provided material instead. - * Use null to clear the material and return renderer to normal - * functionality. - * @param mat The forced material to set, or null to return to normal - */ - public void setForcedMaterial(Material mat) { - forcedMaterial = mat; - } - - /** - * Returns the forced render state previously set with - * {@link #setForcedRenderState(com.jme3.material.RenderState) }. - * @return the forced render state - */ - public RenderState getForcedRenderState() { - return forcedRenderState; - } - - /** - * Set the render state to use for all future objects. - * This overrides the render state set on the material and instead - * forces this render state to be applied for all future materials - * rendered. Set to null to return to normal functionality. - * - * @param forcedRenderState The forced render state to set, or null - * to return to normal - */ - public void setForcedRenderState(RenderState forcedRenderState) { - this.forcedRenderState = forcedRenderState; - } - - /** - * Set the timer that should be used to query the time based - * {@link UniformBinding}s for material world parameters. - * - * @param timer The timer to query time world parameters - */ - public void setTimer(Timer timer) { - this.timer = timer; - } - - /** - * Returns the forced technique name set. - * - * @return the forced technique name set. - * - * @see #setForcedTechnique(java.lang.String) - */ - public String getForcedTechnique() { - return forcedTechnique; - } - - /** - * Sets the forced technique to use when rendering geometries. - * <p> - * If the specified technique name is available on the geometry's - * material, then it is used, otherwise, the - * {@link #setForcedMaterial(com.jme3.material.Material) forced material} is used. - * If a forced material is not set and the forced technique name cannot - * be found on the material, the geometry will <em>not</em> be rendered. - * - * @param forcedTechnique The forced technique name to use, set to null - * to return to normal functionality. - * - * @see #renderGeometry(com.jme3.scene.Geometry) - */ - public void setForcedTechnique(String forcedTechnique) { - this.forcedTechnique = forcedTechnique; - } - - /** - * Enable or disable alpha-to-coverage. - * <p> - * When alpha to coverage is enabled and the renderer implementation - * supports it, then alpha blending will be replaced with alpha dissolve - * if multi-sampling is also set on the renderer. - * This feature allows avoiding of alpha blending artifacts due to - * lack of triangle-level back-to-front sorting. - * - * @param value True to enable alpha-to-coverage, false otherwise. - */ - public void setAlphaToCoverage(boolean value) { - renderer.setAlphaToCoverage(value); - } - - /** - * True if the translucent bucket should automatically be rendered - * by the RenderManager. - * - * @return Whether or not the translucent bucket is rendered. - * - * @see #setHandleTranslucentBucket(boolean) - */ - public boolean isHandleTranslucentBucket() { - return handleTranlucentBucket; - } - - /** - * Enable or disable rendering of the - * {@link Bucket#Translucent translucent bucket} - * by the RenderManager. The default is enabled. - * - * @param handleTranslucentBucket Whether or not the translucent bucket should - * be rendered. - */ - public void setHandleTranslucentBucket(boolean handleTranslucentBucket) { - this.handleTranlucentBucket = handleTranslucentBucket; - } - - /** - * Internal use only. Sets the world matrix to use for future - * rendering. This has no effect unless objects are rendered manually - * using {@link Material#render(com.jme3.scene.Geometry, com.jme3.renderer.RenderManager) }. - * Using {@link #renderGeometry(com.jme3.scene.Geometry) } will - * override this value. - * - * @param mat The world matrix to set - */ - public void setWorldMatrix(Matrix4f mat) { - if (shader) { - worldMatrix.set(mat); - } else { - renderer.setWorldMatrix(mat); - } - } - - /** - * Renders the given geometry. - * <p> - * First the proper world matrix is set, if - * the geometry's {@link Geometry#setIgnoreTransform(boolean) ignore transform} - * feature is enabled, the identity world matrix is used, otherwise, the - * geometry's {@link Geometry#getWorldMatrix() world transform matrix} is used. - * <p> - * Once the world matrix is applied, the proper material is chosen for rendering. - * If a {@link #setForcedMaterial(com.jme3.material.Material) forced material} is - * set on this RenderManager, then it is used for rendering the geometry, - * otherwise, the {@link Geometry#getMaterial() geometry's material} is used. - * <p> - * If a {@link #setForcedTechnique(java.lang.String) forced technique} is - * set on this RenderManager, then it is selected automatically - * on the geometry's material and is used for rendering. Otherwise, one - * of the {@link MaterialDef#getDefaultTechniques() default techniques} is - * used. - * <p> - * If a {@link #setForcedRenderState(com.jme3.material.RenderState) forced - * render state} is set on this RenderManager, then it is used - * for rendering the material, and the material's own render state is ignored. - * Otherwise, the material's render state is used as intended. - * - * @param g The geometry to render - * - * @see Technique - * @see RenderState - * @see Material#selectTechnique(java.lang.String, com.jme3.renderer.RenderManager) - * @see Material#render(com.jme3.scene.Geometry, com.jme3.renderer.RenderManager) - */ - public void renderGeometry(Geometry g) { - if (g.isIgnoreTransform()) { - setWorldMatrix(Matrix4f.IDENTITY); - } else { - setWorldMatrix(g.getWorldMatrix()); - } - - //if forcedTechnique we try to force it for render, - //if it does not exists in the mat def, we check for forcedMaterial and render the geom if not null - //else the geom is not rendered - if (forcedTechnique != null) { - if (g.getMaterial().getMaterialDef().getTechniqueDef(forcedTechnique) != null) { - tmpTech = g.getMaterial().getActiveTechnique() != null ? g.getMaterial().getActiveTechnique().getDef().getName() : "Default"; - g.getMaterial().selectTechnique(forcedTechnique, this); - // use geometry's material - g.getMaterial().render(g, this); - g.getMaterial().selectTechnique(tmpTech, this); - //Reverted this part from revision 6197 - //If forcedTechnique does not exists, and frocedMaterial is not set, the geom MUST NOT be rendered - } else if (forcedMaterial != null) { - // use forced material - forcedMaterial.render(g, this); - } - } else if (forcedMaterial != null) { - // use forced material - forcedMaterial.render(g, this); - } else { - g.getMaterial().render(g, this); - } - } - - /** - * Renders the given GeometryList. - * <p> - * For every geometry in the list, the - * {@link #renderGeometry(com.jme3.scene.Geometry) } method is called. - * - * @param gl The geometry list to render. - * - * @see GeometryList - * @see #renderGeometry(com.jme3.scene.Geometry) - */ - public void renderGeometryList(GeometryList gl) { - for (int i = 0; i < gl.size(); i++) { - renderGeometry(gl.get(i)); - } - } - - /** - * If a spatial is not inside the eye frustum, it - * is still rendered in the shadow frustum (shadow casting queue) - * through this recursive method. - */ - private void renderShadow(Spatial s, RenderQueue rq) { - if (s instanceof Node) { - Node n = (Node) s; - List<Spatial> children = n.getChildren(); - for (int i = 0; i < children.size(); i++) { - renderShadow(children.get(i), rq); - } - } else if (s instanceof Geometry) { - Geometry gm = (Geometry) s; - - RenderQueue.ShadowMode shadowMode = s.getShadowMode(); - if (shadowMode != RenderQueue.ShadowMode.Off && shadowMode != RenderQueue.ShadowMode.Receive) { - //forcing adding to shadow cast mode, culled objects doesn't have to be in the receiver queue - rq.addToShadowQueue(gm, RenderQueue.ShadowMode.Cast); - } - } - } - - /** - * Preloads a scene for rendering. - * <p> - * After invocation of this method, the underlying - * renderer would have uploaded any textures, shaders and meshes - * used by the given scene to the video driver. - * Using this method is useful when wishing to avoid the initial pause - * when rendering a scene for the first time. Note that it is not - * guaranteed that the underlying renderer will actually choose to upload - * the data to the GPU so some pause is still to be expected. - * - * @param scene The scene to preload - */ - public void preloadScene(Spatial scene) { - if (scene instanceof Node) { - // recurse for all children - Node n = (Node) scene; - List<Spatial> children = n.getChildren(); - for (int i = 0; i < children.size(); i++) { - preloadScene(children.get(i)); - } - } else if (scene instanceof Geometry) { - // add to the render queue - Geometry gm = (Geometry) scene; - if (gm.getMaterial() == null) { - throw new IllegalStateException("No material is set for Geometry: " + gm.getName()); - } - - gm.getMaterial().preload(this); - Mesh mesh = gm.getMesh(); - if (mesh != null) { - for (Entry<VertexBuffer> entry : mesh.getBuffers()) { - VertexBuffer buf = entry.getValue(); - if (buf.getData() != null) { - renderer.updateBufferData(buf); - } - } - } - } - } - - /** - * Flattens the given scene graph into the ViewPort's RenderQueue, - * checking for culling as the call goes down the graph recursively. - * <p> - * First, the scene is checked for culling based on the <code>Spatial</code>s - * {@link Spatial#setCullHint(com.jme3.scene.Spatial.CullHint) cull hint}, - * if the camera frustum contains the scene, then this method is recursively - * called on its children. - * <p> - * When the scene's leaves or {@link Geometry geometries} are reached, - * they are each enqueued into the - * {@link ViewPort#getQueue() ViewPort's render queue}. - * <p> - * In addition to enqueuing the visible geometries, this method - * also scenes which cast or receive shadows, by putting them into the - * RenderQueue's - * {@link RenderQueue#addToShadowQueue(com.jme3.scene.Geometry, com.jme3.renderer.queue.RenderQueue.ShadowMode) - * shadow queue}. Each Spatial which has its - * {@link Spatial#setShadowMode(com.jme3.renderer.queue.RenderQueue.ShadowMode) shadow mode} - * set to not off, will be put into the appropriate shadow queue, note that - * this process does not check for frustum culling on any - * {@link ShadowMode#Cast shadow casters}, as they don't have to be - * in the eye camera frustum to cast shadows on objects that are inside it. - * - * @param scene The scene to flatten into the queue - * @param vp The ViewPort provides the {@link ViewPort#getCamera() camera} - * used for culling and the {@link ViewPort#getQueue() queue} used to - * contain the flattened scene graph. - */ - public void renderScene(Spatial scene, ViewPort vp) { - if (scene.getParent() == null) { - vp.getCamera().setPlaneState(0); - } - // check culling first. - if (!scene.checkCulling(vp.getCamera())) { - // move on to shadow-only render - if ((scene.getShadowMode() != RenderQueue.ShadowMode.Off || scene instanceof Node) && scene.getCullHint()!=Spatial.CullHint.Always) { - renderShadow(scene, vp.getQueue()); - } - return; - } - - scene.runControlRender(this, vp); - if (scene instanceof Node) { - // recurse for all children - Node n = (Node) scene; - List<Spatial> children = n.getChildren(); - //saving cam state for culling - int camState = vp.getCamera().getPlaneState(); - for (int i = 0; i < children.size(); i++) { - //restoring cam state before proceeding children recusively - vp.getCamera().setPlaneState(camState); - renderScene(children.get(i), vp); - - } - } else if (scene instanceof Geometry) { - - // add to the render queue - Geometry gm = (Geometry) scene; - if (gm.getMaterial() == null) { - throw new IllegalStateException("No material is set for Geometry: " + gm.getName()); - } - - vp.getQueue().addToQueue(gm, scene.getQueueBucket()); - - // add to shadow queue if needed - RenderQueue.ShadowMode shadowMode = scene.getShadowMode(); - if (shadowMode != RenderQueue.ShadowMode.Off) { - vp.getQueue().addToShadowQueue(gm, shadowMode); - } - } - } - - /** - * Returns the camera currently used for rendering. - * <p> - * The camera can be set with {@link #setCamera(com.jme3.renderer.Camera, boolean) }. - * - * @return the camera currently used for rendering. - */ - public Camera getCurrentCamera() { - return prevCam; - } - - /** - * The renderer implementation used for rendering operations. - * - * @return The renderer implementation - * - * @see #RenderManager(com.jme3.renderer.Renderer) - * @see Renderer - */ - public Renderer getRenderer() { - return renderer; - } - - /** - * Flushes the ViewPort's {@link ViewPort#getQueue() render queue} - * by rendering each of its visible buckets. - * By default the queues will automatically be cleared after rendering, - * so there's no need to clear them manually. - * - * @param vp The ViewPort of which the queue will be flushed - * - * @see RenderQueue#renderQueue(com.jme3.renderer.queue.RenderQueue.Bucket, com.jme3.renderer.RenderManager, com.jme3.renderer.Camera) - * @see #renderGeometryList(com.jme3.renderer.queue.GeometryList) - */ - public void flushQueue(ViewPort vp) { - renderViewPortQueues(vp, true); - } - - /** - * Clears the queue of the given ViewPort. - * Simply calls {@link RenderQueue#clear() } on the ViewPort's - * {@link ViewPort#getQueue() render queue}. - * - * @param vp The ViewPort of which the queue will be cleared. - * - * @see RenderQueue#clear() - * @see ViewPort#getQueue() - */ - public void clearQueue(ViewPort vp) { - vp.getQueue().clear(); - } - - /** - * Render the given viewport queues. - * <p> - * Changes the {@link Renderer#setDepthRange(float, float) depth range} - * appropriately as expected by each queue and then calls - * {@link RenderQueue#renderQueue(com.jme3.renderer.queue.RenderQueue.Bucket, com.jme3.renderer.RenderManager, com.jme3.renderer.Camera, boolean) } - * on the queue. Makes sure to restore the depth range to [0, 1] - * at the end of the call. - * Note that the {@link Bucket#Translucent translucent bucket} is NOT - * rendered by this method. Instead the user should call - * {@link #renderTranslucentQueue(com.jme3.renderer.ViewPort) } - * after this call. - * - * @param vp the viewport of which queue should be rendered - * @param flush If true, the queues will be cleared after - * rendering. - * - * @see RenderQueue - * @see #renderTranslucentQueue(com.jme3.renderer.ViewPort) - */ - public void renderViewPortQueues(ViewPort vp, boolean flush) { - RenderQueue rq = vp.getQueue(); - Camera cam = vp.getCamera(); - boolean depthRangeChanged = false; - - // render opaque objects with default depth range - // opaque objects are sorted front-to-back, reducing overdraw - rq.renderQueue(Bucket.Opaque, this, cam, flush); - - // render the sky, with depth range set to the farthest - if (!rq.isQueueEmpty(Bucket.Sky)) { - renderer.setDepthRange(1, 1); - rq.renderQueue(Bucket.Sky, this, cam, flush); - depthRangeChanged = true; - } - - - // transparent objects are last because they require blending with the - // rest of the scene's objects. Consequently, they are sorted - // back-to-front. - if (!rq.isQueueEmpty(Bucket.Transparent)) { - if (depthRangeChanged) { - renderer.setDepthRange(0, 1); - depthRangeChanged = false; - } - - rq.renderQueue(Bucket.Transparent, this, cam, flush); - } - - if (!rq.isQueueEmpty(Bucket.Gui)) { - renderer.setDepthRange(0, 0); - setCamera(cam, true); - rq.renderQueue(Bucket.Gui, this, cam, flush); - setCamera(cam, false); - depthRangeChanged = true; - } - - // restore range to default - if (depthRangeChanged) { - renderer.setDepthRange(0, 1); - } - } - - /** - * Renders the {@link Bucket#Translucent translucent queue} on the viewPort. - * <p> - * This call does nothing unless {@link #setHandleTranslucentBucket(boolean) } - * is set to true. This method clears the translucent queue after rendering - * it. - * - * @param vp The viewport of which the translucent queue should be rendered. - * - * @see #renderViewPortQueues(com.jme3.renderer.ViewPort, boolean) - * @see #setHandleTranslucentBucket(boolean) - */ - public void renderTranslucentQueue(ViewPort vp) { - RenderQueue rq = vp.getQueue(); - if (!rq.isQueueEmpty(Bucket.Translucent) && handleTranlucentBucket) { - rq.renderQueue(Bucket.Translucent, this, vp.getCamera(), true); - } - } - - private void setViewPort(Camera cam) { - // this will make sure to update viewport only if needed - if (cam != prevCam || cam.isViewportChanged()) { - viewX = (int) (cam.getViewPortLeft() * cam.getWidth()); - viewY = (int) (cam.getViewPortBottom() * cam.getHeight()); - viewWidth = (int) ((cam.getViewPortRight() - cam.getViewPortLeft()) * cam.getWidth()); - viewHeight = (int) ((cam.getViewPortTop() - cam.getViewPortBottom()) * cam.getHeight()); - renderer.setViewPort(viewX, viewY, viewWidth, viewHeight); - renderer.setClipRect(viewX, viewY, viewWidth, viewHeight); - cam.clearViewportChanged(); - prevCam = cam; - -// float translateX = viewWidth == viewX ? 0 : -(viewWidth + viewX) / (viewWidth - viewX); -// float translateY = viewHeight == viewY ? 0 : -(viewHeight + viewY) / (viewHeight - viewY); -// float scaleX = viewWidth == viewX ? 1f : 2f / (viewWidth - viewX); -// float scaleY = viewHeight == viewY ? 1f : 2f / (viewHeight - viewY); -// -// orthoMatrix.loadIdentity(); -// orthoMatrix.setTranslation(translateX, translateY, 0); -// orthoMatrix.setScale(scaleX, scaleY, 0); - - orthoMatrix.loadIdentity(); - orthoMatrix.setTranslation(-1f, -1f, 0f); - orthoMatrix.setScale(2f / cam.getWidth(), 2f / cam.getHeight(), 0f); - } - } - - private void setViewProjection(Camera cam, boolean ortho) { - if (shader) { - if (ortho) { - viewMatrix.set(Matrix4f.IDENTITY); - projMatrix.set(orthoMatrix); - viewProjMatrix.set(orthoMatrix); - } else { - viewMatrix.set(cam.getViewMatrix()); - projMatrix.set(cam.getProjectionMatrix()); - viewProjMatrix.set(cam.getViewProjectionMatrix()); - } - - camLoc.set(cam.getLocation()); - cam.getLeft(camLeft); - cam.getUp(camUp); - cam.getDirection(camDir); - - near = cam.getFrustumNear(); - far = cam.getFrustumFar(); - } else { - if (ortho) { - renderer.setViewProjectionMatrices(Matrix4f.IDENTITY, orthoMatrix); - } else { - renderer.setViewProjectionMatrices(cam.getViewMatrix(), - cam.getProjectionMatrix()); - } - - } - } - - /** - * Set the camera to use for rendering. - * <p> - * First, the camera's - * {@link Camera#setViewPort(float, float, float, float) view port parameters} - * are applied. Then, the camera's {@link Camera#getViewMatrix() view} and - * {@link Camera#getProjectionMatrix() projection} matrices are set - * on the renderer. If <code>ortho</code> is <code>true</code>, then - * instead of using the camera's view and projection matrices, an ortho - * matrix is computed and used instead of the view projection matrix. - * The ortho matrix converts from the range (0 ~ Width, 0 ~ Height, -1 ~ +1) - * to the clip range (-1 ~ +1, -1 ~ +1, -1 ~ +1). - * - * @param cam The camera to set - * @param ortho True if to use orthographic projection (for GUI rendering), - * false if to use the camera's view and projection matrices. - */ - public void setCamera(Camera cam, boolean ortho) { - setViewPort(cam); - setViewProjection(cam, ortho); - } - - /** - * Draws the viewport but without notifying {@link SceneProcessor scene - * processors} of any rendering events. - * - * @param vp The ViewPort to render - * - * @see #renderViewPort(com.jme3.renderer.ViewPort, float) - */ - public void renderViewPortRaw(ViewPort vp) { - setCamera(vp.getCamera(), false); - List<Spatial> scenes = vp.getScenes(); - for (int i = scenes.size() - 1; i >= 0; i--) { - renderScene(scenes.get(i), vp); - } - flushQueue(vp); - } - - /** - * Renders the {@link ViewPort}. - * <p> - * If the ViewPort is {@link ViewPort#isEnabled() disabled}, this method - * returns immediately. Otherwise, the ViewPort is rendered by - * the following process:<br> - * <ul> - * <li>All {@link SceneProcessor scene processors} that are attached - * to the ViewPort are {@link SceneProcessor#initialize(com.jme3.renderer.RenderManager, com.jme3.renderer.ViewPort) initialized}. - * </li> - * <li>The SceneProcessors' {@link SceneProcessor#preFrame(float) } method - * is called.</li> - * <li>The ViewPort's {@link ViewPort#getOutputFrameBuffer() output framebuffer} - * is set on the Renderer</li> - * <li>The camera is set on the renderer, including its view port parameters. - * (see {@link #setCamera(com.jme3.renderer.Camera, boolean) })</li> - * <li>Any buffers that the ViewPort requests to be cleared are cleared - * and the {@link ViewPort#getBackgroundColor() background color} is set</li> - * <li>Every scene that is attached to the ViewPort is flattened into - * the ViewPort's render queue - * (see {@link #renderViewPortQueues(com.jme3.renderer.ViewPort, boolean) }) - * </li> - * <li>The SceneProcessors' {@link SceneProcessor#postQueue(com.jme3.renderer.queue.RenderQueue) } - * method is called.</li> - * <li>The render queue is sorted and then flushed, sending - * rendering commands to the underlying Renderer implementation. - * (see {@link #flushQueue(com.jme3.renderer.ViewPort) })</li> - * <li>The SceneProcessors' {@link SceneProcessor#postFrame(com.jme3.texture.FrameBuffer) } - * method is called.</li> - * <li>The translucent queue of the ViewPort is sorted and then flushed - * (see {@link #renderTranslucentQueue(com.jme3.renderer.ViewPort) })</li> - * <li>If any objects remained in the render queue, they are removed - * from the queue. This is generally objects added to the - * {@link RenderQueue#renderShadowQueue(com.jme3.renderer.queue.RenderQueue.ShadowMode, com.jme3.renderer.RenderManager, com.jme3.renderer.Camera, boolean) - * shadow queue} - * which were not rendered because of a missing shadow renderer.</li> - * </ul> - * - * @param vp - * @param tpf - */ - public void renderViewPort(ViewPort vp, float tpf) { - if (!vp.isEnabled()) { - return; - } - List<SceneProcessor> processors = vp.getProcessors(); - if (processors.isEmpty()) { - processors = null; - } - - if (processors != null) { - for (SceneProcessor proc : processors) { - if (!proc.isInitialized()) { - proc.initialize(this, vp); - } - proc.preFrame(tpf); - } - } - - renderer.setFrameBuffer(vp.getOutputFrameBuffer()); - setCamera(vp.getCamera(), false); - if (vp.isClearDepth() || vp.isClearColor() || vp.isClearStencil()) { - if (vp.isClearColor()) { - renderer.setBackgroundColor(vp.getBackgroundColor()); - } - renderer.clearBuffers(vp.isClearColor(), - vp.isClearDepth(), - vp.isClearStencil()); - } - - List<Spatial> scenes = vp.getScenes(); - for (int i = scenes.size() - 1; i >= 0; i--) { - renderScene(scenes.get(i), vp); - } - - if (processors != null) { - for (SceneProcessor proc : processors) { - proc.postQueue(vp.getQueue()); - } - } - - flushQueue(vp); - - if (processors != null) { - for (SceneProcessor proc : processors) { - proc.postFrame(vp.getOutputFrameBuffer()); - } - } - //renders the translucent objects queue after processors have been rendered - renderTranslucentQueue(vp); - // clear any remaining spatials that were not rendered. - clearQueue(vp); - } - - /** - * Called by the application to render any ViewPorts - * added to this RenderManager. - * <p> - * Renders any viewports that were added using the following methods: - * <ul> - * <li>{@link #createPreView(java.lang.String, com.jme3.renderer.Camera) }</li> - * <li>{@link #createMainView(java.lang.String, com.jme3.renderer.Camera) }</li> - * <li>{@link #createPostView(java.lang.String, com.jme3.renderer.Camera) }</li> - * </ul> - * - * @param tpf Time per frame value - */ - public void render(float tpf, boolean mainFrameBufferActive) { - if (renderer instanceof NullRenderer) { - return; - } - - this.shader = renderer.getCaps().contains(Caps.GLSL100); - - for (int i = 0; i < preViewPorts.size(); i++) { - ViewPort vp = preViewPorts.get(i); - if (vp.getOutputFrameBuffer() != null || mainFrameBufferActive){ - renderViewPort(vp, tpf); - } - } - for (int i = 0; i < viewPorts.size(); i++) { - ViewPort vp = viewPorts.get(i); - if (vp.getOutputFrameBuffer() != null || mainFrameBufferActive){ - renderViewPort(vp, tpf); - } - } - for (int i = 0; i < postViewPorts.size(); i++) { - ViewPort vp = postViewPorts.get(i); - if (vp.getOutputFrameBuffer() != null || mainFrameBufferActive){ - renderViewPort(vp, tpf); - } - } - } -} +/* + * Copyright (c) 2009-2012 jMonkeyEngine + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are + * met: + * + * * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * + * * Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * * Neither the name of 'jMonkeyEngine' nor the names of its contributors + * may be used to endorse or promote products derived from this software + * without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED + * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR + * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR + * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, + * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, + * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR + * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF + * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING + * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ +package com.jme3.renderer; + +import com.jme3.material.Material; +import com.jme3.material.MaterialDef; +import com.jme3.material.RenderState; +import com.jme3.material.Technique; +import com.jme3.math.*; +import com.jme3.post.SceneProcessor; +import com.jme3.renderer.queue.GeometryList; +import com.jme3.renderer.queue.RenderQueue; +import com.jme3.renderer.queue.RenderQueue.Bucket; +import com.jme3.renderer.queue.RenderQueue.ShadowMode; +import com.jme3.scene.*; +import com.jme3.shader.Uniform; +import com.jme3.shader.UniformBinding; +import com.jme3.shader.VarType; +import com.jme3.system.NullRenderer; +import com.jme3.system.Timer; +import com.jme3.util.IntMap.Entry; +import com.jme3.util.TempVars; +import java.util.ArrayList; +import java.util.Collections; +import java.util.List; +import java.util.logging.Logger; + +/** + * <code>RenderManager</code> is a high-level rendering interface that is + * above the Renderer implementation. RenderManager takes care + * of rendering the scene graphs attached to each viewport and + * handling SceneProcessors. + * + * @see SceneProcessor + * @see ViewPort + * @see Spatial + */ +public class RenderManager { + + private static final Logger logger = Logger.getLogger(RenderManager.class.getName()); + + private Renderer renderer; + private Timer timer; + private ArrayList<ViewPort> preViewPorts = new ArrayList<ViewPort>(); + private ArrayList<ViewPort> viewPorts = new ArrayList<ViewPort>(); + private ArrayList<ViewPort> postViewPorts = new ArrayList<ViewPort>(); + private Camera prevCam = null; + private Material forcedMaterial = null; + private String forcedTechnique = null; + private RenderState forcedRenderState = null; + private boolean shader; + private int viewX, viewY, viewWidth, viewHeight; + private float near, far; + private Matrix4f orthoMatrix = new Matrix4f(); + private Matrix4f viewMatrix = new Matrix4f(); + private Matrix4f projMatrix = new Matrix4f(); + private Matrix4f viewProjMatrix = new Matrix4f(); + private Matrix4f worldMatrix = new Matrix4f(); + private Vector3f camUp = new Vector3f(), + camLeft = new Vector3f(), + camDir = new Vector3f(), + camLoc = new Vector3f(); + //temp technique + private String tmpTech; + private boolean handleTranlucentBucket = true; + + /** + * Create a high-level rendering interface over the + * low-level rendering interface. + * @param renderer + */ + public RenderManager(Renderer renderer) { + this.renderer = renderer; + //this.shader = renderer.getCaps().contains(Caps.GLSL100); + } + + /** + * Returns the pre ViewPort with the given name. + * + * @param viewName The name of the pre ViewPort to look up + * @return The ViewPort, or null if not found. + * + * @see #createPreView(java.lang.String, com.jme3.renderer.Camera) + */ + public ViewPort getPreView(String viewName) { + for (int i = 0; i < preViewPorts.size(); i++) { + if (preViewPorts.get(i).getName().equals(viewName)) { + return preViewPorts.get(i); + } + } + return null; + } + + /** + * Removes the specified pre ViewPort. + * + * @param view The pre ViewPort to remove + * @return True if the ViewPort was removed successfully. + * + * @see #createPreView(java.lang.String, com.jme3.renderer.Camera) + */ + public boolean removePreView(ViewPort view) { + return preViewPorts.remove(view); + } + + /** + * Returns the main ViewPort with the given name. + * + * @param viewName The name of the main ViewPort to look up + * @return The ViewPort, or null if not found. + * + * @see #createMainView(java.lang.String, com.jme3.renderer.Camera) + */ + public ViewPort getMainView(String viewName) { + for (int i = 0; i < viewPorts.size(); i++) { + if (viewPorts.get(i).getName().equals(viewName)) { + return viewPorts.get(i); + } + } + return null; + } + + /** + * Removes the main ViewPort with the specified name. + * + * @param viewName The main ViewPort name to remove + * @return True if the ViewPort was removed successfully. + * + * @see #createMainView(java.lang.String, com.jme3.renderer.Camera) + */ + public boolean removeMainView(String viewName) { + for (int i = 0; i < viewPorts.size(); i++) { + if (viewPorts.get(i).getName().equals(viewName)) { + viewPorts.remove(i); + return true; + } + } + return false; + } + + /** + * Removes the specified main ViewPort. + * + * @param view The main ViewPort to remove + * @return True if the ViewPort was removed successfully. + * + * @see #createMainView(java.lang.String, com.jme3.renderer.Camera) + */ + public boolean removeMainView(ViewPort view) { + return viewPorts.remove(view); + } + + /** + * Returns the post ViewPort with the given name. + * + * @param viewName The name of the post ViewPort to look up + * @return The ViewPort, or null if not found. + * + * @see #createPostView(java.lang.String, com.jme3.renderer.Camera) + */ + public ViewPort getPostView(String viewName) { + for (int i = 0; i < postViewPorts.size(); i++) { + if (postViewPorts.get(i).getName().equals(viewName)) { + return postViewPorts.get(i); + } + } + return null; + } + + /** + * Removes the post ViewPort with the specified name. + * + * @param viewName The post ViewPort name to remove + * @return True if the ViewPort was removed successfully. + * + * @see #createPostView(java.lang.String, com.jme3.renderer.Camera) + */ + public boolean removePostView(String viewName) { + for (int i = 0; i < postViewPorts.size(); i++) { + if (postViewPorts.get(i).getName().equals(viewName)) { + postViewPorts.remove(i); + + return true; + } + } + return false; + } + + /** + * Removes the specified post ViewPort. + * + * @param view The post ViewPort to remove + * @return True if the ViewPort was removed successfully. + * + * @see #createPostView(java.lang.String, com.jme3.renderer.Camera) + */ + public boolean removePostView(ViewPort view) { + return postViewPorts.remove(view); + } + + /** + * Returns a read-only list of all pre ViewPorts + * @return a read-only list of all pre ViewPorts + * @see #createPreView(java.lang.String, com.jme3.renderer.Camera) + */ + public List<ViewPort> getPreViews() { + return Collections.unmodifiableList(preViewPorts); + } + + /** + * Returns a read-only list of all main ViewPorts + * @return a read-only list of all main ViewPorts + * @see #createMainView(java.lang.String, com.jme3.renderer.Camera) + */ + public List<ViewPort> getMainViews() { + return Collections.unmodifiableList(viewPorts); + } + + /** + * Returns a read-only list of all post ViewPorts + * @return a read-only list of all post ViewPorts + * @see #createPostView(java.lang.String, com.jme3.renderer.Camera) + */ + public List<ViewPort> getPostViews() { + return Collections.unmodifiableList(postViewPorts); + } + + /** + * Creates a new pre ViewPort, to display the given camera's content. + * <p> + * The view will be processed before the main and post viewports. + */ + public ViewPort createPreView(String viewName, Camera cam) { + ViewPort vp = new ViewPort(viewName, cam); + preViewPorts.add(vp); + return vp; + } + + /** + * Creates a new main ViewPort, to display the given camera's content. + * <p> + * The view will be processed before the post viewports but after + * the pre viewports. + */ + public ViewPort createMainView(String viewName, Camera cam) { + ViewPort vp = new ViewPort(viewName, cam); + viewPorts.add(vp); + return vp; + } + + /** + * Creates a new post ViewPort, to display the given camera's content. + * <p> + * The view will be processed after the pre and main viewports. + */ + public ViewPort createPostView(String viewName, Camera cam) { + ViewPort vp = new ViewPort(viewName, cam); + postViewPorts.add(vp); + return vp; + } + + private void notifyReshape(ViewPort vp, int w, int h) { + List<SceneProcessor> processors = vp.getProcessors(); + for (SceneProcessor proc : processors) { + if (!proc.isInitialized()) { + proc.initialize(this, vp); + } else { + proc.reshape(vp, w, h); + } + } + } + + /** + * Internal use only. + * Updates the resolution of all on-screen cameras to match + * the given width and height. + */ + public void notifyReshape(int w, int h) { + for (ViewPort vp : preViewPorts) { + if (vp.getOutputFrameBuffer() == null) { + Camera cam = vp.getCamera(); + cam.resize(w, h, true); + } + notifyReshape(vp, w, h); + } + for (ViewPort vp : viewPorts) { + if (vp.getOutputFrameBuffer() == null) { + Camera cam = vp.getCamera(); + cam.resize(w, h, true); + } + notifyReshape(vp, w, h); + } + for (ViewPort vp : postViewPorts) { + if (vp.getOutputFrameBuffer() == null) { + Camera cam = vp.getCamera(); + cam.resize(w, h, true); + } + notifyReshape(vp, w, h); + } + } + + /** + * Internal use only. + * Updates the given list of uniforms with {@link UniformBinding uniform bindings} + * based on the current world state. + */ + public void updateUniformBindings(List<Uniform> params) { + // assums worldMatrix is properly set. + TempVars vars = TempVars.get(); + + Matrix4f tempMat4 = vars.tempMat4; + Matrix3f tempMat3 = vars.tempMat3; + Vector2f tempVec2 = vars.vect2d; + Quaternion tempVec4 = vars.quat1; + + for (int i = 0; i < params.size(); i++) { + Uniform u = params.get(i); + switch (u.getBinding()) { + case WorldMatrix: + u.setValue(VarType.Matrix4, worldMatrix); + break; + case ViewMatrix: + u.setValue(VarType.Matrix4, viewMatrix); + break; + case ProjectionMatrix: + u.setValue(VarType.Matrix4, projMatrix); + break; + case ViewProjectionMatrix: + u.setValue(VarType.Matrix4, viewProjMatrix); + break; + case WorldViewMatrix: + tempMat4.set(viewMatrix); + tempMat4.multLocal(worldMatrix); + u.setValue(VarType.Matrix4, tempMat4); + break; + case NormalMatrix: + tempMat4.set(viewMatrix); + tempMat4.multLocal(worldMatrix); + tempMat4.toRotationMatrix(tempMat3); + tempMat3.invertLocal(); + tempMat3.transposeLocal(); + u.setValue(VarType.Matrix3, tempMat3); + break; + case WorldViewProjectionMatrix: + tempMat4.set(viewProjMatrix); + tempMat4.multLocal(worldMatrix); + u.setValue(VarType.Matrix4, tempMat4); + break; + case WorldMatrixInverse: + tempMat4.set(worldMatrix); + tempMat4.invertLocal(); + u.setValue(VarType.Matrix4, tempMat4); + break; + case WorldMatrixInverseTranspose: + worldMatrix.toRotationMatrix(tempMat3); + tempMat3.invertLocal().transposeLocal(); + u.setValue(VarType.Matrix3, tempMat3); + break; + case ViewMatrixInverse: + tempMat4.set(viewMatrix); + tempMat4.invertLocal(); + u.setValue(VarType.Matrix4, tempMat4); + break; + case ProjectionMatrixInverse: + tempMat4.set(projMatrix); + tempMat4.invertLocal(); + u.setValue(VarType.Matrix4, tempMat4); + break; + case ViewProjectionMatrixInverse: + tempMat4.set(viewProjMatrix); + tempMat4.invertLocal(); + u.setValue(VarType.Matrix4, tempMat4); + break; + case WorldViewMatrixInverse: + tempMat4.set(viewMatrix); + tempMat4.multLocal(worldMatrix); + tempMat4.invertLocal(); + u.setValue(VarType.Matrix4, tempMat4); + break; + case NormalMatrixInverse: + tempMat4.set(viewMatrix); + tempMat4.multLocal(worldMatrix); + tempMat4.toRotationMatrix(tempMat3); + tempMat3.invertLocal(); + tempMat3.transposeLocal(); + tempMat3.invertLocal(); + u.setValue(VarType.Matrix3, tempMat3); + break; + case WorldViewProjectionMatrixInverse: + tempMat4.set(viewProjMatrix); + tempMat4.multLocal(worldMatrix); + tempMat4.invertLocal(); + u.setValue(VarType.Matrix4, tempMat4); + break; + case ViewPort: + tempVec4.set(viewX, viewY, viewWidth, viewHeight); + u.setValue(VarType.Vector4, tempVec4); + break; + case Resolution: + tempVec2.set(viewWidth, viewHeight); + u.setValue(VarType.Vector2, tempVec2); + break; + case ResolutionInverse: + tempVec2.set(1f / viewWidth, 1f / viewHeight); + u.setValue(VarType.Vector2, tempVec2); + break; + case Aspect: + float aspect = ((float) viewWidth) / viewHeight; + u.setValue(VarType.Float, aspect); + break; + case FrustumNearFar: + tempVec2.set(near, far); + u.setValue(VarType.Vector2, tempVec2); + break; + case CameraPosition: + u.setValue(VarType.Vector3, camLoc); + break; + case CameraDirection: + u.setValue(VarType.Vector3, camDir); + break; + case CameraLeft: + u.setValue(VarType.Vector3, camLeft); + break; + case CameraUp: + u.setValue(VarType.Vector3, camUp); + break; + case Time: + u.setValue(VarType.Float, timer.getTimeInSeconds()); + break; + case Tpf: + u.setValue(VarType.Float, timer.getTimePerFrame()); + break; + case FrameRate: + u.setValue(VarType.Float, timer.getFrameRate()); + break; + } + } + + vars.release(); + } + + /** + * Set the material to use to render all future objects. + * This overrides the material set on the geometry and renders + * with the provided material instead. + * Use null to clear the material and return renderer to normal + * functionality. + * @param mat The forced material to set, or null to return to normal + */ + public void setForcedMaterial(Material mat) { + forcedMaterial = mat; + } + + /** + * Returns the forced render state previously set with + * {@link #setForcedRenderState(com.jme3.material.RenderState) }. + * @return the forced render state + */ + public RenderState getForcedRenderState() { + return forcedRenderState; + } + + /** + * Set the render state to use for all future objects. + * This overrides the render state set on the material and instead + * forces this render state to be applied for all future materials + * rendered. Set to null to return to normal functionality. + * + * @param forcedRenderState The forced render state to set, or null + * to return to normal + */ + public void setForcedRenderState(RenderState forcedRenderState) { + this.forcedRenderState = forcedRenderState; + } + + /** + * Set the timer that should be used to query the time based + * {@link UniformBinding}s for material world parameters. + * + * @param timer The timer to query time world parameters + */ + public void setTimer(Timer timer) { + this.timer = timer; + } + + /** + * Returns the forced technique name set. + * + * @return the forced technique name set. + * + * @see #setForcedTechnique(java.lang.String) + */ + public String getForcedTechnique() { + return forcedTechnique; + } + + /** + * Sets the forced technique to use when rendering geometries. + * <p> + * If the specified technique name is available on the geometry's + * material, then it is used, otherwise, the + * {@link #setForcedMaterial(com.jme3.material.Material) forced material} is used. + * If a forced material is not set and the forced technique name cannot + * be found on the material, the geometry will <em>not</em> be rendered. + * + * @param forcedTechnique The forced technique name to use, set to null + * to return to normal functionality. + * + * @see #renderGeometry(com.jme3.scene.Geometry) + */ + public void setForcedTechnique(String forcedTechnique) { + this.forcedTechnique = forcedTechnique; + } + + /** + * Enable or disable alpha-to-coverage. + * <p> + * When alpha to coverage is enabled and the renderer implementation + * supports it, then alpha blending will be replaced with alpha dissolve + * if multi-sampling is also set on the renderer. + * This feature allows avoiding of alpha blending artifacts due to + * lack of triangle-level back-to-front sorting. + * + * @param value True to enable alpha-to-coverage, false otherwise. + */ + public void setAlphaToCoverage(boolean value) { + renderer.setAlphaToCoverage(value); + } + + /** + * True if the translucent bucket should automatically be rendered + * by the RenderManager. + * + * @return Whether or not the translucent bucket is rendered. + * + * @see #setHandleTranslucentBucket(boolean) + */ + public boolean isHandleTranslucentBucket() { + return handleTranlucentBucket; + } + + /** + * Enable or disable rendering of the + * {@link Bucket#Translucent translucent bucket} + * by the RenderManager. The default is enabled. + * + * @param handleTranslucentBucket Whether or not the translucent bucket should + * be rendered. + */ + public void setHandleTranslucentBucket(boolean handleTranslucentBucket) { + this.handleTranlucentBucket = handleTranslucentBucket; + } + + /** + * Internal use only. Sets the world matrix to use for future + * rendering. This has no effect unless objects are rendered manually + * using {@link Material#render(com.jme3.scene.Geometry, com.jme3.renderer.RenderManager) }. + * Using {@link #renderGeometry(com.jme3.scene.Geometry) } will + * override this value. + * + * @param mat The world matrix to set + */ + public void setWorldMatrix(Matrix4f mat) { + if (shader) { + worldMatrix.set(mat); + } else { + renderer.setWorldMatrix(mat); + } + } + + /** + * Renders the given geometry. + * <p> + * First the proper world matrix is set, if + * the geometry's {@link Geometry#setIgnoreTransform(boolean) ignore transform} + * feature is enabled, the identity world matrix is used, otherwise, the + * geometry's {@link Geometry#getWorldMatrix() world transform matrix} is used. + * <p> + * Once the world matrix is applied, the proper material is chosen for rendering. + * If a {@link #setForcedMaterial(com.jme3.material.Material) forced material} is + * set on this RenderManager, then it is used for rendering the geometry, + * otherwise, the {@link Geometry#getMaterial() geometry's material} is used. + * <p> + * If a {@link #setForcedTechnique(java.lang.String) forced technique} is + * set on this RenderManager, then it is selected automatically + * on the geometry's material and is used for rendering. Otherwise, one + * of the {@link MaterialDef#getDefaultTechniques() default techniques} is + * used. + * <p> + * If a {@link #setForcedRenderState(com.jme3.material.RenderState) forced + * render state} is set on this RenderManager, then it is used + * for rendering the material, and the material's own render state is ignored. + * Otherwise, the material's render state is used as intended. + * + * @param g The geometry to render + * + * @see Technique + * @see RenderState + * @see Material#selectTechnique(java.lang.String, com.jme3.renderer.RenderManager) + * @see Material#render(com.jme3.scene.Geometry, com.jme3.renderer.RenderManager) + */ + public void renderGeometry(Geometry g) { + if (g.isIgnoreTransform()) { + setWorldMatrix(Matrix4f.IDENTITY); + } else { + setWorldMatrix(g.getWorldMatrix()); + } + + //if forcedTechnique we try to force it for render, + //if it does not exists in the mat def, we check for forcedMaterial and render the geom if not null + //else the geom is not rendered + if (forcedTechnique != null) { + if (g.getMaterial().getMaterialDef().getTechniqueDef(forcedTechnique) != null) { + tmpTech = g.getMaterial().getActiveTechnique() != null ? g.getMaterial().getActiveTechnique().getDef().getName() : "Default"; + g.getMaterial().selectTechnique(forcedTechnique, this); + // use geometry's material + g.getMaterial().render(g, this); + g.getMaterial().selectTechnique(tmpTech, this); + //Reverted this part from revision 6197 + //If forcedTechnique does not exists, and frocedMaterial is not set, the geom MUST NOT be rendered + } else if (forcedMaterial != null) { + // use forced material + forcedMaterial.render(g, this); + } + } else if (forcedMaterial != null) { + // use forced material + forcedMaterial.render(g, this); + } else { + g.getMaterial().render(g, this); + } + } + + /** + * Renders the given GeometryList. + * <p> + * For every geometry in the list, the + * {@link #renderGeometry(com.jme3.scene.Geometry) } method is called. + * + * @param gl The geometry list to render. + * + * @see GeometryList + * @see #renderGeometry(com.jme3.scene.Geometry) + */ + public void renderGeometryList(GeometryList gl) { + for (int i = 0; i < gl.size(); i++) { + renderGeometry(gl.get(i)); + } + } + + /** + * If a spatial is not inside the eye frustum, it + * is still rendered in the shadow frustum (shadow casting queue) + * through this recursive method. + */ + private void renderShadow(Spatial s, RenderQueue rq) { + if (s instanceof Node) { + Node n = (Node) s; + List<Spatial> children = n.getChildren(); + for (int i = 0; i < children.size(); i++) { + renderShadow(children.get(i), rq); + } + } else if (s instanceof Geometry) { + Geometry gm = (Geometry) s; + + RenderQueue.ShadowMode shadowMode = s.getShadowMode(); + if (shadowMode != RenderQueue.ShadowMode.Off && shadowMode != RenderQueue.ShadowMode.Receive) { + //forcing adding to shadow cast mode, culled objects doesn't have to be in the receiver queue + rq.addToShadowQueue(gm, RenderQueue.ShadowMode.Cast); + } + } + } + + /** + * Preloads a scene for rendering. + * <p> + * After invocation of this method, the underlying + * renderer would have uploaded any textures, shaders and meshes + * used by the given scene to the video driver. + * Using this method is useful when wishing to avoid the initial pause + * when rendering a scene for the first time. Note that it is not + * guaranteed that the underlying renderer will actually choose to upload + * the data to the GPU so some pause is still to be expected. + * + * @param scene The scene to preload + */ + public void preloadScene(Spatial scene) { + if (scene instanceof Node) { + // recurse for all children + Node n = (Node) scene; + List<Spatial> children = n.getChildren(); + for (int i = 0; i < children.size(); i++) { + preloadScene(children.get(i)); + } + } else if (scene instanceof Geometry) { + // add to the render queue + Geometry gm = (Geometry) scene; + if (gm.getMaterial() == null) { + throw new IllegalStateException("No material is set for Geometry: " + gm.getName()); + } + + gm.getMaterial().preload(this); + Mesh mesh = gm.getMesh(); + if (mesh != null) { + for (VertexBuffer vb : mesh.getBufferList().getArray()) { + if (vb.getData() != null) { + renderer.updateBufferData(vb); + } + } + } + } + } + + /** + * Flattens the given scene graph into the ViewPort's RenderQueue, + * checking for culling as the call goes down the graph recursively. + * <p> + * First, the scene is checked for culling based on the <code>Spatial</code>s + * {@link Spatial#setCullHint(com.jme3.scene.Spatial.CullHint) cull hint}, + * if the camera frustum contains the scene, then this method is recursively + * called on its children. + * <p> + * When the scene's leaves or {@link Geometry geometries} are reached, + * they are each enqueued into the + * {@link ViewPort#getQueue() ViewPort's render queue}. + * <p> + * In addition to enqueuing the visible geometries, this method + * also scenes which cast or receive shadows, by putting them into the + * RenderQueue's + * {@link RenderQueue#addToShadowQueue(com.jme3.scene.Geometry, com.jme3.renderer.queue.RenderQueue.ShadowMode) + * shadow queue}. Each Spatial which has its + * {@link Spatial#setShadowMode(com.jme3.renderer.queue.RenderQueue.ShadowMode) shadow mode} + * set to not off, will be put into the appropriate shadow queue, note that + * this process does not check for frustum culling on any + * {@link ShadowMode#Cast shadow casters}, as they don't have to be + * in the eye camera frustum to cast shadows on objects that are inside it. + * + * @param scene The scene to flatten into the queue + * @param vp The ViewPort provides the {@link ViewPort#getCamera() camera} + * used for culling and the {@link ViewPort#getQueue() queue} used to + * contain the flattened scene graph. + */ + public void renderScene(Spatial scene, ViewPort vp) { + if (scene.getParent() == null) { + vp.getCamera().setPlaneState(0); + } + // check culling first. + if (!scene.checkCulling(vp.getCamera())) { + // move on to shadow-only render + if ((scene.getShadowMode() != RenderQueue.ShadowMode.Off || scene instanceof Node) && scene.getCullHint()!=Spatial.CullHint.Always) { + renderShadow(scene, vp.getQueue()); + } + return; + } + + scene.runControlRender(this, vp); + if (scene instanceof Node) { + // recurse for all children + Node n = (Node) scene; + List<Spatial> children = n.getChildren(); + //saving cam state for culling + int camState = vp.getCamera().getPlaneState(); + for (int i = 0; i < children.size(); i++) { + //restoring cam state before proceeding children recusively + vp.getCamera().setPlaneState(camState); + renderScene(children.get(i), vp); + + } + } else if (scene instanceof Geometry) { + + // add to the render queue + Geometry gm = (Geometry) scene; + if (gm.getMaterial() == null) { + throw new IllegalStateException("No material is set for Geometry: " + gm.getName()); + } + + vp.getQueue().addToQueue(gm, scene.getQueueBucket()); + + // add to shadow queue if needed + RenderQueue.ShadowMode shadowMode = scene.getShadowMode(); + if (shadowMode != RenderQueue.ShadowMode.Off) { + vp.getQueue().addToShadowQueue(gm, shadowMode); + } + } + } + + /** + * Returns the camera currently used for rendering. + * <p> + * The camera can be set with {@link #setCamera(com.jme3.renderer.Camera, boolean) }. + * + * @return the camera currently used for rendering. + */ + public Camera getCurrentCamera() { + return prevCam; + } + + /** + * The renderer implementation used for rendering operations. + * + * @return The renderer implementation + * + * @see #RenderManager(com.jme3.renderer.Renderer) + * @see Renderer + */ + public Renderer getRenderer() { + return renderer; + } + + /** + * Flushes the ViewPort's {@link ViewPort#getQueue() render queue} + * by rendering each of its visible buckets. + * By default the queues will automatically be cleared after rendering, + * so there's no need to clear them manually. + * + * @param vp The ViewPort of which the queue will be flushed + * + * @see RenderQueue#renderQueue(com.jme3.renderer.queue.RenderQueue.Bucket, com.jme3.renderer.RenderManager, com.jme3.renderer.Camera) + * @see #renderGeometryList(com.jme3.renderer.queue.GeometryList) + */ + public void flushQueue(ViewPort vp) { + renderViewPortQueues(vp, true); + } + + /** + * Clears the queue of the given ViewPort. + * Simply calls {@link RenderQueue#clear() } on the ViewPort's + * {@link ViewPort#getQueue() render queue}. + * + * @param vp The ViewPort of which the queue will be cleared. + * + * @see RenderQueue#clear() + * @see ViewPort#getQueue() + */ + public void clearQueue(ViewPort vp) { + vp.getQueue().clear(); + } + + /** + * Render the given viewport queues. + * <p> + * Changes the {@link Renderer#setDepthRange(float, float) depth range} + * appropriately as expected by each queue and then calls + * {@link RenderQueue#renderQueue(com.jme3.renderer.queue.RenderQueue.Bucket, com.jme3.renderer.RenderManager, com.jme3.renderer.Camera, boolean) } + * on the queue. Makes sure to restore the depth range to [0, 1] + * at the end of the call. + * Note that the {@link Bucket#Translucent translucent bucket} is NOT + * rendered by this method. Instead the user should call + * {@link #renderTranslucentQueue(com.jme3.renderer.ViewPort) } + * after this call. + * + * @param vp the viewport of which queue should be rendered + * @param flush If true, the queues will be cleared after + * rendering. + * + * @see RenderQueue + * @see #renderTranslucentQueue(com.jme3.renderer.ViewPort) + */ + public void renderViewPortQueues(ViewPort vp, boolean flush) { + RenderQueue rq = vp.getQueue(); + Camera cam = vp.getCamera(); + boolean depthRangeChanged = false; + + // render opaque objects with default depth range + // opaque objects are sorted front-to-back, reducing overdraw + rq.renderQueue(Bucket.Opaque, this, cam, flush); + + // render the sky, with depth range set to the farthest + if (!rq.isQueueEmpty(Bucket.Sky)) { + renderer.setDepthRange(1, 1); + rq.renderQueue(Bucket.Sky, this, cam, flush); + depthRangeChanged = true; + } + + + // transparent objects are last because they require blending with the + // rest of the scene's objects. Consequently, they are sorted + // back-to-front. + if (!rq.isQueueEmpty(Bucket.Transparent)) { + if (depthRangeChanged) { + renderer.setDepthRange(0, 1); + depthRangeChanged = false; + } + + rq.renderQueue(Bucket.Transparent, this, cam, flush); + } + + if (!rq.isQueueEmpty(Bucket.Gui)) { + renderer.setDepthRange(0, 0); + setCamera(cam, true); + rq.renderQueue(Bucket.Gui, this, cam, flush); + setCamera(cam, false); + depthRangeChanged = true; + } + + // restore range to default + if (depthRangeChanged) { + renderer.setDepthRange(0, 1); + } + } + + /** + * Renders the {@link Bucket#Translucent translucent queue} on the viewPort. + * <p> + * This call does nothing unless {@link #setHandleTranslucentBucket(boolean) } + * is set to true. This method clears the translucent queue after rendering + * it. + * + * @param vp The viewport of which the translucent queue should be rendered. + * + * @see #renderViewPortQueues(com.jme3.renderer.ViewPort, boolean) + * @see #setHandleTranslucentBucket(boolean) + */ + public void renderTranslucentQueue(ViewPort vp) { + RenderQueue rq = vp.getQueue(); + if (!rq.isQueueEmpty(Bucket.Translucent) && handleTranlucentBucket) { + rq.renderQueue(Bucket.Translucent, this, vp.getCamera(), true); + } + } + + private void setViewPort(Camera cam) { + // this will make sure to update viewport only if needed + if (cam != prevCam || cam.isViewportChanged()) { + viewX = (int) (cam.getViewPortLeft() * cam.getWidth()); + viewY = (int) (cam.getViewPortBottom() * cam.getHeight()); + viewWidth = (int) ((cam.getViewPortRight() - cam.getViewPortLeft()) * cam.getWidth()); + viewHeight = (int) ((cam.getViewPortTop() - cam.getViewPortBottom()) * cam.getHeight()); + renderer.setViewPort(viewX, viewY, viewWidth, viewHeight); + renderer.setClipRect(viewX, viewY, viewWidth, viewHeight); + cam.clearViewportChanged(); + prevCam = cam; + +// float translateX = viewWidth == viewX ? 0 : -(viewWidth + viewX) / (viewWidth - viewX); +// float translateY = viewHeight == viewY ? 0 : -(viewHeight + viewY) / (viewHeight - viewY); +// float scaleX = viewWidth == viewX ? 1f : 2f / (viewWidth - viewX); +// float scaleY = viewHeight == viewY ? 1f : 2f / (viewHeight - viewY); +// +// orthoMatrix.loadIdentity(); +// orthoMatrix.setTranslation(translateX, translateY, 0); +// orthoMatrix.setScale(scaleX, scaleY, 0); + + orthoMatrix.loadIdentity(); + orthoMatrix.setTranslation(-1f, -1f, 0f); + orthoMatrix.setScale(2f / cam.getWidth(), 2f / cam.getHeight(), 0f); + } + } + + private void setViewProjection(Camera cam, boolean ortho) { + if (shader) { + if (ortho) { + viewMatrix.set(Matrix4f.IDENTITY); + projMatrix.set(orthoMatrix); + viewProjMatrix.set(orthoMatrix); + } else { + viewMatrix.set(cam.getViewMatrix()); + projMatrix.set(cam.getProjectionMatrix()); + viewProjMatrix.set(cam.getViewProjectionMatrix()); + } + + camLoc.set(cam.getLocation()); + cam.getLeft(camLeft); + cam.getUp(camUp); + cam.getDirection(camDir); + + near = cam.getFrustumNear(); + far = cam.getFrustumFar(); + } else { + if (ortho) { + renderer.setViewProjectionMatrices(Matrix4f.IDENTITY, orthoMatrix); + } else { + renderer.setViewProjectionMatrices(cam.getViewMatrix(), + cam.getProjectionMatrix()); + } + + } + } + + /** + * Set the camera to use for rendering. + * <p> + * First, the camera's + * {@link Camera#setViewPort(float, float, float, float) view port parameters} + * are applied. Then, the camera's {@link Camera#getViewMatrix() view} and + * {@link Camera#getProjectionMatrix() projection} matrices are set + * on the renderer. If <code>ortho</code> is <code>true</code>, then + * instead of using the camera's view and projection matrices, an ortho + * matrix is computed and used instead of the view projection matrix. + * The ortho matrix converts from the range (0 ~ Width, 0 ~ Height, -1 ~ +1) + * to the clip range (-1 ~ +1, -1 ~ +1, -1 ~ +1). + * + * @param cam The camera to set + * @param ortho True if to use orthographic projection (for GUI rendering), + * false if to use the camera's view and projection matrices. + */ + public void setCamera(Camera cam, boolean ortho) { + setViewPort(cam); + setViewProjection(cam, ortho); + } + + /** + * Draws the viewport but without notifying {@link SceneProcessor scene + * processors} of any rendering events. + * + * @param vp The ViewPort to render + * + * @see #renderViewPort(com.jme3.renderer.ViewPort, float) + */ + public void renderViewPortRaw(ViewPort vp) { + setCamera(vp.getCamera(), false); + List<Spatial> scenes = vp.getScenes(); + for (int i = scenes.size() - 1; i >= 0; i--) { + renderScene(scenes.get(i), vp); + } + flushQueue(vp); + } + + /** + * Renders the {@link ViewPort}. + * <p> + * If the ViewPort is {@link ViewPort#isEnabled() disabled}, this method + * returns immediately. Otherwise, the ViewPort is rendered by + * the following process:<br> + * <ul> + * <li>All {@link SceneProcessor scene processors} that are attached + * to the ViewPort are {@link SceneProcessor#initialize(com.jme3.renderer.RenderManager, com.jme3.renderer.ViewPort) initialized}. + * </li> + * <li>The SceneProcessors' {@link SceneProcessor#preFrame(float) } method + * is called.</li> + * <li>The ViewPort's {@link ViewPort#getOutputFrameBuffer() output framebuffer} + * is set on the Renderer</li> + * <li>The camera is set on the renderer, including its view port parameters. + * (see {@link #setCamera(com.jme3.renderer.Camera, boolean) })</li> + * <li>Any buffers that the ViewPort requests to be cleared are cleared + * and the {@link ViewPort#getBackgroundColor() background color} is set</li> + * <li>Every scene that is attached to the ViewPort is flattened into + * the ViewPort's render queue + * (see {@link #renderViewPortQueues(com.jme3.renderer.ViewPort, boolean) }) + * </li> + * <li>The SceneProcessors' {@link SceneProcessor#postQueue(com.jme3.renderer.queue.RenderQueue) } + * method is called.</li> + * <li>The render queue is sorted and then flushed, sending + * rendering commands to the underlying Renderer implementation. + * (see {@link #flushQueue(com.jme3.renderer.ViewPort) })</li> + * <li>The SceneProcessors' {@link SceneProcessor#postFrame(com.jme3.texture.FrameBuffer) } + * method is called.</li> + * <li>The translucent queue of the ViewPort is sorted and then flushed + * (see {@link #renderTranslucentQueue(com.jme3.renderer.ViewPort) })</li> + * <li>If any objects remained in the render queue, they are removed + * from the queue. This is generally objects added to the + * {@link RenderQueue#renderShadowQueue(com.jme3.renderer.queue.RenderQueue.ShadowMode, com.jme3.renderer.RenderManager, com.jme3.renderer.Camera, boolean) + * shadow queue} + * which were not rendered because of a missing shadow renderer.</li> + * </ul> + * + * @param vp + * @param tpf + */ + public void renderViewPort(ViewPort vp, float tpf) { + if (!vp.isEnabled()) { + return; + } + List<SceneProcessor> processors = vp.getProcessors(); + if (processors.isEmpty()) { + processors = null; + } + + if (processors != null) { + for (SceneProcessor proc : processors) { + if (!proc.isInitialized()) { + proc.initialize(this, vp); + } + proc.preFrame(tpf); + } + } + + renderer.setFrameBuffer(vp.getOutputFrameBuffer()); + setCamera(vp.getCamera(), false); + if (vp.isClearDepth() || vp.isClearColor() || vp.isClearStencil()) { + if (vp.isClearColor()) { + renderer.setBackgroundColor(vp.getBackgroundColor()); + } + renderer.clearBuffers(vp.isClearColor(), + vp.isClearDepth(), + vp.isClearStencil()); + } + + List<Spatial> scenes = vp.getScenes(); + for (int i = scenes.size() - 1; i >= 0; i--) { + renderScene(scenes.get(i), vp); + } + + if (processors != null) { + for (SceneProcessor proc : processors) { + proc.postQueue(vp.getQueue()); + } + } + + flushQueue(vp); + + if (processors != null) { + for (SceneProcessor proc : processors) { + proc.postFrame(vp.getOutputFrameBuffer()); + } + } + //renders the translucent objects queue after processors have been rendered + renderTranslucentQueue(vp); + // clear any remaining spatials that were not rendered. + clearQueue(vp); + } + + /** + * Called by the application to render any ViewPorts + * added to this RenderManager. + * <p> + * Renders any viewports that were added using the following methods: + * <ul> + * <li>{@link #createPreView(java.lang.String, com.jme3.renderer.Camera) }</li> + * <li>{@link #createMainView(java.lang.String, com.jme3.renderer.Camera) }</li> + * <li>{@link #createPostView(java.lang.String, com.jme3.renderer.Camera) }</li> + * </ul> + * + * @param tpf Time per frame value + */ + public void render(float tpf, boolean mainFrameBufferActive) { + if (renderer instanceof NullRenderer) { + return; + } + + this.shader = renderer.getCaps().contains(Caps.GLSL100); + + for (int i = 0; i < preViewPorts.size(); i++) { + ViewPort vp = preViewPorts.get(i); + if (vp.getOutputFrameBuffer() != null || mainFrameBufferActive){ + renderViewPort(vp, tpf); + } + } + for (int i = 0; i < viewPorts.size(); i++) { + ViewPort vp = viewPorts.get(i); + if (vp.getOutputFrameBuffer() != null || mainFrameBufferActive){ + renderViewPort(vp, tpf); + } + } + for (int i = 0; i < postViewPorts.size(); i++) { + ViewPort vp = postViewPorts.get(i); + if (vp.getOutputFrameBuffer() != null || mainFrameBufferActive){ + renderViewPort(vp, tpf); + } + } + } +} diff --git a/engine/src/core/com/jme3/scene/BatchNode.java b/engine/src/core/com/jme3/scene/BatchNode.java index bc1b2cb..303de7a 100644 --- a/engine/src/core/com/jme3/scene/BatchNode.java +++ b/engine/src/core/com/jme3/scene/BatchNode.java
@@ -46,6 +46,7 @@ import java.util.HashMap; import java.util.List; import java.util.Map; +import java.util.Set; import java.util.logging.Level; import java.util.logging.Logger; @@ -190,10 +191,10 @@ } batches.clear(); } - - for (Material material : matMap.keySet()) { + for (Map.Entry<Material, List<Geometry>> entry : matMap.entrySet()) { Mesh m = new Mesh(); - List<Geometry> list = matMap.get(material); + Material material = entry.getKey(); + List<Geometry> list = entry.getValue(); nbGeoms += list.size(); if (!needsFullRebatch) { list.add(batches.get(material).geometry); @@ -408,9 +409,9 @@ throw new UnsupportedOperationException(); } - for (Entry<VertexBuffer> entry : geom.getMesh().getBuffers()) { - compsForBuf[entry.getKey()] = entry.getValue().getNumComponents(); - formatForBuf[entry.getKey()] = entry.getValue().getFormat(); + for (VertexBuffer vb : geom.getMesh().getBufferList().getArray()) { + compsForBuf[vb.getBufferType().ordinal()] = vb.getNumComponents(); + formatForBuf[vb.getBufferType().ordinal()] = vb.getFormat(); } if (mode != null && mode != listMode) { diff --git a/engine/src/core/com/jme3/scene/Mesh.java b/engine/src/core/com/jme3/scene/Mesh.java index 6c587f2..036e525 100644 --- a/engine/src/core/com/jme3/scene/Mesh.java +++ b/engine/src/core/com/jme3/scene/Mesh.java
@@ -43,7 +43,6 @@ import com.jme3.math.Triangle; import com.jme3.math.Vector2f; import com.jme3.math.Vector3f; -import com.jme3.scene.VertexBuffer; import com.jme3.scene.VertexBuffer.Format; import com.jme3.scene.VertexBuffer.Type; import com.jme3.scene.VertexBuffer.Usage; @@ -55,8 +54,6 @@ import java.io.IOException; import java.nio.*; import java.util.ArrayList; -import java.util.HashSet; -import java.util.Set; /** * <code>Mesh</code> is used to store rendering data. @@ -237,9 +234,9 @@ clone.buffers = new IntMap<VertexBuffer>(); clone.buffersList = new SafeArrayList<VertexBuffer>(VertexBuffer.class); - for (Entry<VertexBuffer> ent : buffers){ - VertexBuffer bufClone = ent.getValue().clone(); - clone.buffers.put(ent.getKey(), bufClone); + for (VertexBuffer vb : buffersList.getArray()){ + VertexBuffer bufClone = vb.clone(); + clone.buffers.put(vb.getBufferType().ordinal(), bufClone); clone.buffersList.add(bufClone); } @@ -540,8 +537,8 @@ * for all {@link VertexBuffer vertex buffers} on this Mesh. */ public void setStatic() { - for (Entry<VertexBuffer> entry : buffers){ - entry.getValue().setUsage(Usage.Static); + for (VertexBuffer vb : buffersList.getArray()){ + vb.setUsage(Usage.Static); } } @@ -551,8 +548,8 @@ * for all {@link VertexBuffer vertex buffers} on this Mesh. */ public void setDynamic() { - for (Entry<VertexBuffer> entry : buffers){ - entry.getValue().setUsage(Usage.Dynamic); + for (VertexBuffer vb : buffersList.getArray()){ + vb.setUsage(Usage.Dynamic); } } @@ -562,8 +559,8 @@ * for all {@link VertexBuffer vertex buffers} on this Mesh. */ public void setStreamed(){ - for (Entry<VertexBuffer> entry : buffers){ - entry.getValue().setUsage(Usage.Stream); + for (VertexBuffer vb : buffersList.getArray()){ + vb.setUsage(Usage.Stream); } } @@ -572,11 +569,11 @@ * Some GPUs may prefer the data in this format, however it is a good idea * to <em>avoid</em> using this method as it disables some engine features. */ + @Deprecated public void setInterleaved(){ ArrayList<VertexBuffer> vbs = new ArrayList<VertexBuffer>(); - for (Entry<VertexBuffer> entry : buffers){ - vbs.add(entry.getValue()); - } + vbs.addAll(buffersList); + // ArrayList<VertexBuffer> vbs = new ArrayList<VertexBuffer>(buffers.values()); // index buffer not included when interleaving vbs.remove(getBuffer(Type.Index)); @@ -860,6 +857,65 @@ } /** + * Sets the {@link VertexBuffer} on the mesh. + * This will update the vertex/triangle counts if needed. + * + * @param vb The buffer to set + * @throws IllegalArgumentException If the buffer type is already set + */ + public void setBuffer(VertexBuffer vb){ + if (buffers.containsKey(vb.getBufferType().ordinal())) + throw new IllegalArgumentException("Buffer type already set: "+vb.getBufferType()); + + buffers.put(vb.getBufferType().ordinal(), vb); + buffersList.add(vb); + updateCounts(); + } + + /** + * Unsets the {@link VertexBuffer} set on this mesh + * with the given type. Does nothing if the vertex buffer type is not set + * initially. + * + * @param type The buffer type to remove + */ + public void clearBuffer(VertexBuffer.Type type){ + VertexBuffer vb = buffers.remove(type.ordinal()); + if (vb != null){ + buffersList.remove(vb); + updateCounts(); + } + } + + /** + * Creates a {@link VertexBuffer} for the mesh or modifies + * the existing one per the parameters given. + * + * @param type The type of the buffer + * @param components Number of components + * @param format Data format + * @param buf The buffer data + * + * @throws UnsupportedOperationException If the buffer already set is + * incompatible with the parameters given. + */ + public void setBuffer(Type type, int components, Format format, Buffer buf){ + VertexBuffer vb = buffers.get(type.ordinal()); + if (vb == null){ + vb = new VertexBuffer(type); + vb.setupData(Usage.Dynamic, components, format, buf); + setBuffer(vb); + }else{ + if (vb.getNumComponents() != components || vb.getFormat() != format){ + throw new UnsupportedOperationException("The buffer already set " + + "is incompatible with the given parameters"); + } + vb.updateData(buf); + updateCounts(); + } + } + + /** * Set a floating point {@link VertexBuffer} on the mesh. * * @param type The type of {@link VertexBuffer}, @@ -871,21 +927,7 @@ * @param buf The floating point data to contain */ public void setBuffer(Type type, int components, FloatBuffer buf) { -// VertexBuffer vb = buffers.get(type); - VertexBuffer vb = buffers.get(type.ordinal()); - if (vb == null){ - if (buf == null) - return; - - vb = new VertexBuffer(type); - vb.setupData(Usage.Dynamic, components, Format.Float, buf); -// buffers.put(type, vb); - buffers.put(type.ordinal(), vb); - buffersList.add(vb); - }else{ - vb.setupData(Usage.Dynamic, components, Format.Float, buf); - } - updateCounts(); + setBuffer(type, components, Format.Float, buf); } public void setBuffer(Type type, int components, float[] buf){ @@ -893,14 +935,7 @@ } public void setBuffer(Type type, int components, IntBuffer buf) { - VertexBuffer vb = buffers.get(type.ordinal()); - if (vb == null){ - vb = new VertexBuffer(type); - vb.setupData(Usage.Dynamic, components, Format.UnsignedInt, buf); - buffers.put(type.ordinal(), vb); - buffersList.add(vb); - updateCounts(); - } + setBuffer(type, components, Format.UnsignedInt, buf); } public void setBuffer(Type type, int components, int[] buf){ @@ -908,14 +943,7 @@ } public void setBuffer(Type type, int components, ShortBuffer buf) { - VertexBuffer vb = buffers.get(type.ordinal()); - if (vb == null){ - vb = new VertexBuffer(type); - vb.setupData(Usage.Dynamic, components, Format.UnsignedShort, buf); - buffers.put(type.ordinal(), vb); - buffersList.add(vb); - updateCounts(); - } + setBuffer(type, components, Format.UnsignedShort, buf); } public void setBuffer(Type type, int components, byte[] buf){ @@ -923,38 +951,7 @@ } public void setBuffer(Type type, int components, ByteBuffer buf) { - VertexBuffer vb = buffers.get(type.ordinal()); - if (vb == null){ - vb = new VertexBuffer(type); - vb.setupData(Usage.Dynamic, components, Format.UnsignedByte, buf); - buffers.put(type.ordinal(), vb); - buffersList.add(vb); - updateCounts(); - } - } - - public void setBuffer(VertexBuffer vb){ - if (buffers.containsKey(vb.getBufferType().ordinal())) - throw new IllegalArgumentException("Buffer type already set: "+vb.getBufferType()); - - buffers.put(vb.getBufferType().ordinal(), vb); - buffersList.add(vb); - updateCounts(); - } - - /** - * Clears or unsets the {@link VertexBuffer} set on this mesh - * with the given type. - * Does nothing if the vertex buffer type is not set initially - * - * @param type The type to remove - */ - public void clearBuffer(VertexBuffer.Type type){ - VertexBuffer vb = buffers.remove(type.ordinal()); - if (vb != null){ - buffersList.remove(vb); - updateCounts(); - } + setBuffer(type, components, Format.UnsignedByte, buf); } public void setBuffer(Type type, int components, short[] buf){ diff --git a/engine/src/core/com/jme3/shader/UniformBinding.java b/engine/src/core/com/jme3/shader/UniformBinding.java index bdca35b..6bd4ca9 100644 --- a/engine/src/core/com/jme3/shader/UniformBinding.java +++ b/engine/src/core/com/jme3/shader/UniformBinding.java
@@ -1,162 +1,176 @@ -/* - * Copyright (c) 2009-2010 jMonkeyEngine - * All rights reserved. - * - * Redistribution and use in source and binary forms, with or without - * modification, are permitted provided that the following conditions are - * met: - * - * * Redistributions of source code must retain the above copyright - * notice, this list of conditions and the following disclaimer. - * - * * Redistributions in binary form must reproduce the above copyright - * notice, this list of conditions and the following disclaimer in the - * documentation and/or other materials provided with the distribution. - * - * * Neither the name of 'jMonkeyEngine' nor the names of its contributors - * may be used to endorse or promote products derived from this software - * without specific prior written permission. - * - * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS - * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED - * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR - * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR - * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, - * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, - * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR - * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF - * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING - * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS - * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. - */ - -package com.jme3.shader; - -public enum UniformBinding { - - /** - * The world matrix. Converts Model space to World space. - * Type: mat4 - */ - WorldMatrix, - - /** - * The view matrix. Converts World space to View space. - * Type: mat4 - */ - ViewMatrix, - - /** - * The projection matrix. Converts View space to Clip/Projection space. - * Type: mat4 - */ - ProjectionMatrix, - - /** - * The world view matrix. Converts Model space to View space. - * Type: mat4 - */ - WorldViewMatrix, - - /** - * The normal matrix. The inverse transpose of the worldview matrix. - * Converts normals from model space to view space. - * Type: mat3 - */ - NormalMatrix, - - /** - * The world view projection matrix. Converts Model space to Clip/Projection - * space. - * Type: mat4 - */ - WorldViewProjectionMatrix, - - /** - * The view projection matrix. Converts Model space to Clip/Projection - * space. - * Type: mat4 - */ - ViewProjectionMatrix, - - - WorldMatrixInverse, - ViewMatrixInverse, - ProjectionMatrixInverse, - ViewProjectionMatrixInverse, - WorldViewMatrixInverse, - NormalMatrixInverse, - WorldViewProjectionMatrixInverse, - - /** - * Contains the four viewport parameters in this order: - * X = Left, - * Y = Top, - * Z = Right, - * W = Bottom. - * Type: vec4 - */ - ViewPort, - - /** - * The near and far values for the camera frustum. - * X = Near - * Y = Far. - * Type: vec2 - */ - FrustumNearFar, - - /** - * The width and height of the camera. - * Type: vec2 - */ - Resolution, - - /** - * Aspect ratio of the resolution currently set. Width/Height. - * Type: float - */ - Aspect, - - /** - * Camera position in world space. - * Type: vec3 - */ - CameraPosition, - - /** - * Direction of the camera. - * Type: vec3 - */ - CameraDirection, - - /** - * Left vector of the camera. - * Type: vec3 - */ - CameraLeft, - - /** - * Up vector of the camera. - * Type: vec3 - */ - CameraUp, - - /** - * Time in seconds since the application was started. - * Type: float - */ - Time, - - /** - * Time in seconds that the last frame took. - * Type: float - */ - Tpf, - - /** - * Frames per second. - * Type: float - */ - FrameRate, -} +/* + * Copyright (c) 2009-2010 jMonkeyEngine + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are + * met: + * + * * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * + * * Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * * Neither the name of 'jMonkeyEngine' nor the names of its contributors + * may be used to endorse or promote products derived from this software + * without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED + * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR + * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR + * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, + * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, + * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR + * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF + * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING + * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +package com.jme3.shader; + +public enum UniformBinding { + + /** + * The world matrix. Converts Model space to World space. + * Type: mat4 + */ + WorldMatrix, + + /** + * The view matrix. Converts World space to View space. + * Type: mat4 + */ + ViewMatrix, + + /** + * The projection matrix. Converts View space to Clip/Projection space. + * Type: mat4 + */ + ProjectionMatrix, + + /** + * The world view matrix. Converts Model space to View space. + * Type: mat4 + */ + WorldViewMatrix, + + /** + * The normal matrix. The inverse transpose of the worldview matrix. + * Converts normals from model space to view space. + * Type: mat3 + */ + NormalMatrix, + + /** + * The world view projection matrix. Converts Model space to Clip/Projection + * space. + * Type: mat4 + */ + WorldViewProjectionMatrix, + + /** + * The view projection matrix. Converts World space to Clip/Projection + * space. + * Type: mat4 + */ + ViewProjectionMatrix, + + /** + * The world matrix inverse transpose. Converts a normals from Model space + * to world space. + * Type: mat3 + */ + WorldMatrixInverseTranspose, + + + + WorldMatrixInverse, + ViewMatrixInverse, + ProjectionMatrixInverse, + ViewProjectionMatrixInverse, + WorldViewMatrixInverse, + NormalMatrixInverse, + WorldViewProjectionMatrixInverse, + + /** + * Contains the four viewport parameters in this order: + * X = Left, + * Y = Top, + * Z = Right, + * W = Bottom. + * Type: vec4 + */ + ViewPort, + + /** + * The near and far values for the camera frustum. + * X = Near + * Y = Far. + * Type: vec2 + */ + FrustumNearFar, + + /** + * The width and height of the camera. + * Type: vec2 + */ + Resolution, + + /** + * The inverse of the resolution, 1/width and 1/height. + * Type: vec2 + */ + ResolutionInverse, + + /** + * Aspect ratio of the resolution currently set. Width/Height. + * Type: float + */ + Aspect, + + /** + * Camera position in world space. + * Type: vec3 + */ + CameraPosition, + + /** + * Direction of the camera. + * Type: vec3 + */ + CameraDirection, + + /** + * Left vector of the camera. + * Type: vec3 + */ + CameraLeft, + + /** + * Up vector of the camera. + * Type: vec3 + */ + CameraUp, + + /** + * Time in seconds since the application was started. + * Type: float + */ + Time, + + /** + * Time in seconds that the last frame took. + * Type: float + */ + Tpf, + + /** + * Frames per second. + * Type: float + */ + FrameRate, +} diff --git a/engine/src/core/com/jme3/system/JmeSystem.java b/engine/src/core/com/jme3/system/JmeSystem.java index 1a13a42..2e2d79a 100644 --- a/engine/src/core/com/jme3/system/JmeSystem.java +++ b/engine/src/core/com/jme3/system/JmeSystem.java
@@ -1,136 +1,147 @@ -/* - * Copyright (c) 2009-2010 jMonkeyEngine - * All rights reserved. - * - * Redistribution and use in source and binary forms, with or without - * modification, are permitted provided that the following conditions are - * met: - * - * * Redistributions of source code must retain the above copyright - * notice, this list of conditions and the following disclaimer. - * - * * Redistributions in binary form must reproduce the above copyright - * notice, this list of conditions and the following disclaimer in the - * documentation and/or other materials provided with the distribution. - * - * * Neither the name of 'jMonkeyEngine' nor the names of its contributors - * may be used to endorse or promote products derived from this software - * without specific prior written permission. - * - * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS - * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED - * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR - * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR - * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, - * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, - * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR - * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF - * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING - * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS - * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. - */ -package com.jme3.system; - -import com.jme3.asset.AssetManager; -import com.jme3.audio.AudioRenderer; -import java.io.File; -import java.io.InputStream; -import java.net.URL; -import java.util.logging.Level; -import java.util.logging.Logger; - -public class JmeSystem { - - private static JmeSystemDelegate systemDelegate; - - public static void setSystemDelegate(JmeSystemDelegate systemDelegate) { - JmeSystem.systemDelegate = systemDelegate; - } - - public static synchronized File getStorageFolder() { - checkDelegate(); - return systemDelegate.getStorageFolder(); - } - - public static String getFullName() { - checkDelegate(); - return systemDelegate.getFullName(); - } - - public static InputStream getResourceAsStream(String name) { - checkDelegate(); - return systemDelegate.getResourceAsStream(name); - } - - public static URL getResource(String name) { - checkDelegate(); - return systemDelegate.getResource(name); - } - - public static boolean trackDirectMemory() { - checkDelegate(); - return systemDelegate.trackDirectMemory(); - } - - public static void setLowPermissions(boolean lowPerm) { - checkDelegate(); - systemDelegate.setLowPermissions(lowPerm); - } - - public static boolean isLowPermissions() { - checkDelegate(); - return systemDelegate.isLowPermissions(); - } - - public static AssetManager newAssetManager(URL configFile) { - checkDelegate(); - return systemDelegate.newAssetManager(configFile); - } - - public static AssetManager newAssetManager() { - checkDelegate(); - return systemDelegate.newAssetManager(); - } - - public static boolean showSettingsDialog(AppSettings sourceSettings, final boolean loadFromRegistry) { - checkDelegate(); - return systemDelegate.showSettingsDialog(sourceSettings, loadFromRegistry); - } - - public static Platform getPlatform() { - checkDelegate(); - return systemDelegate.getPlatform(); - } - - public static JmeContext newContext(AppSettings settings, JmeContext.Type contextType) { - checkDelegate(); - return systemDelegate.newContext(settings, contextType); - } - - public static AudioRenderer newAudioRenderer(AppSettings settings) { - checkDelegate(); - return systemDelegate.newAudioRenderer(settings); - } - - public static void initialize(AppSettings settings) { - checkDelegate(); - systemDelegate.initialize(settings); - } - - @SuppressWarnings("unchecked") - private static void checkDelegate() { - if (systemDelegate == null) { - Class<JmeSystemDelegate> systemDelegateClass; - try { - systemDelegateClass = (Class<JmeSystemDelegate>) Class.forName("com.jme3.system.JmeDesktopSystem"); - systemDelegate = systemDelegateClass.newInstance(); - } catch (InstantiationException ex) { - Logger.getLogger(JmeSystem.class.getName()).log(Level.SEVERE, "No JmeSystemDelegate specified, cannot instantiate default JmeDesktopSystem:\n{0}", ex); - } catch (IllegalAccessException ex) { - Logger.getLogger(JmeSystem.class.getName()).log(Level.SEVERE, "No JmeSystemDelegate specified, cannot instantiate default JmeDesktopSystem:\n{0}", ex); - } catch (ClassNotFoundException ex) { - Logger.getLogger(JmeSystem.class.getName()).log(Level.SEVERE, "No JmeSystemDelegate specified, cannot instantiate default JmeDesktopSystem:\n{0}", ex); - } - } - } -} +/* + * Copyright (c) 2009-2010 jMonkeyEngine + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are + * met: + * + * * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * + * * Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * * Neither the name of 'jMonkeyEngine' nor the names of its contributors + * may be used to endorse or promote products derived from this software + * without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED + * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR + * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR + * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, + * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, + * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR + * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF + * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING + * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ +package com.jme3.system; + +import com.jme3.asset.AssetManager; +import com.jme3.audio.AudioRenderer; +import com.jme3.input.SoftTextDialogInput; +import java.io.File; +import java.io.InputStream; +import java.net.URL; +import java.util.logging.Level; +import java.util.logging.Logger; + +public class JmeSystem { + + private static JmeSystemDelegate systemDelegate; + + public static void setSystemDelegate(JmeSystemDelegate systemDelegate) { + JmeSystem.systemDelegate = systemDelegate; + } + + public static synchronized File getStorageFolder() { + checkDelegate(); + return systemDelegate.getStorageFolder(); + } + + public static String getFullName() { + checkDelegate(); + return systemDelegate.getFullName(); + } + + public static InputStream getResourceAsStream(String name) { + checkDelegate(); + return systemDelegate.getResourceAsStream(name); + } + + public static URL getResource(String name) { + checkDelegate(); + return systemDelegate.getResource(name); + } + + public static boolean trackDirectMemory() { + checkDelegate(); + return systemDelegate.trackDirectMemory(); + } + + public static void setLowPermissions(boolean lowPerm) { + checkDelegate(); + systemDelegate.setLowPermissions(lowPerm); + } + + public static boolean isLowPermissions() { + checkDelegate(); + return systemDelegate.isLowPermissions(); + } + + public static void setSoftTextDialogInput(SoftTextDialogInput input) { + checkDelegate(); + systemDelegate.setSoftTextDialogInput(input); + } + + public static SoftTextDialogInput getSoftTextDialogInput() { + checkDelegate(); + return systemDelegate.getSoftTextDialogInput(); + } + + public static AssetManager newAssetManager(URL configFile) { + checkDelegate(); + return systemDelegate.newAssetManager(configFile); + } + + public static AssetManager newAssetManager() { + checkDelegate(); + return systemDelegate.newAssetManager(); + } + + public static boolean showSettingsDialog(AppSettings sourceSettings, final boolean loadFromRegistry) { + checkDelegate(); + return systemDelegate.showSettingsDialog(sourceSettings, loadFromRegistry); + } + + public static Platform getPlatform() { + checkDelegate(); + return systemDelegate.getPlatform(); + } + + public static JmeContext newContext(AppSettings settings, JmeContext.Type contextType) { + checkDelegate(); + return systemDelegate.newContext(settings, contextType); + } + + public static AudioRenderer newAudioRenderer(AppSettings settings) { + checkDelegate(); + return systemDelegate.newAudioRenderer(settings); + } + + public static void initialize(AppSettings settings) { + checkDelegate(); + systemDelegate.initialize(settings); + } + + @SuppressWarnings("unchecked") + private static void checkDelegate() { + if (systemDelegate == null) { + Class<JmeSystemDelegate> systemDelegateClass; + try { + systemDelegateClass = (Class<JmeSystemDelegate>) Class.forName("com.jme3.system.JmeDesktopSystem"); + systemDelegate = systemDelegateClass.newInstance(); + } catch (InstantiationException ex) { + Logger.getLogger(JmeSystem.class.getName()).log(Level.SEVERE, "No JmeSystemDelegate specified, cannot instantiate default JmeDesktopSystem:\n{0}", ex); + } catch (IllegalAccessException ex) { + Logger.getLogger(JmeSystem.class.getName()).log(Level.SEVERE, "No JmeSystemDelegate specified, cannot instantiate default JmeDesktopSystem:\n{0}", ex); + } catch (ClassNotFoundException ex) { + Logger.getLogger(JmeSystem.class.getName()).log(Level.SEVERE, "No JmeSystemDelegate specified, cannot instantiate default JmeDesktopSystem:\n{0}", ex); + } + } + } +} diff --git a/engine/src/core/com/jme3/system/JmeSystemDelegate.java b/engine/src/core/com/jme3/system/JmeSystemDelegate.java index 60265ae..e0f6fa5 100644 --- a/engine/src/core/com/jme3/system/JmeSystemDelegate.java +++ b/engine/src/core/com/jme3/system/JmeSystemDelegate.java
@@ -1,140 +1,149 @@ -/* - * Copyright (c) 2009-2010 jMonkeyEngine - * All rights reserved. - * - * Redistribution and use in source and binary forms, with or without - * modification, are permitted provided that the following conditions are - * met: - * - * * Redistributions of source code must retain the above copyright - * notice, this list of conditions and the following disclaimer. - * - * * Redistributions in binary form must reproduce the above copyright - * notice, this list of conditions and the following disclaimer in the - * documentation and/or other materials provided with the distribution. - * - * * Neither the name of 'jMonkeyEngine' nor the names of its contributors - * may be used to endorse or promote products derived from this software - * without specific prior written permission. - * - * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS - * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED - * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR - * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR - * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, - * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, - * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR - * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF - * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING - * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS - * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. - */ -package com.jme3.system; - -import com.jme3.asset.AssetManager; -import com.jme3.audio.AudioRenderer; -import java.io.File; -import java.io.InputStream; -import java.net.URL; -import java.util.logging.Logger; - -/** - * - * @author Kirill Vainer, normenhansen - */ -public abstract class JmeSystemDelegate { - - protected final Logger logger = Logger.getLogger(JmeSystem.class.getName()); - protected boolean initialized = false; - protected boolean lowPermissions = false; - protected File storageFolder = null; - - public synchronized File getStorageFolder() { - if (lowPermissions) { - throw new UnsupportedOperationException("File system access restricted"); - } - if (storageFolder == null) { - // Initialize storage folder - storageFolder = new File(System.getProperty("user.home"), ".jme3"); - if (!storageFolder.exists()) { - storageFolder.mkdir(); - } - } - return storageFolder; - } - - public String getFullName() { - return JmeVersion.FULL_NAME; - } - - public InputStream getResourceAsStream(String name) { - return this.getClass().getResourceAsStream(name); - } - - public URL getResource(String name) { - return this.getClass().getResource(name); - } - - public boolean trackDirectMemory() { - return false; - } - - public void setLowPermissions(boolean lowPerm) { - lowPermissions = lowPerm; - } - - public boolean isLowPermissions() { - return lowPermissions; - } - - public abstract AssetManager newAssetManager(URL configFile); - - public abstract AssetManager newAssetManager(); - - public abstract boolean showSettingsDialog(AppSettings sourceSettings, boolean loadFromRegistry); - - private boolean is64Bit(String arch) { - if (arch.equals("x86")) { - return false; - } else if (arch.equals("amd64")) { - return true; - } else if (arch.equals("x86_64")) { - return true; - } else if (arch.equals("ppc") || arch.equals("PowerPC")) { - return false; - } else if (arch.equals("ppc64")) { - return true; - } else if (arch.equals("i386") || arch.equals("i686")) { - return false; - } else if (arch.equals("universal")) { - return false; - } else { - throw new UnsupportedOperationException("Unsupported architecture: " + arch); - } - } - - public Platform getPlatform() { - String os = System.getProperty("os.name").toLowerCase(); - String arch = System.getProperty("os.arch").toLowerCase(); - boolean is64 = is64Bit(arch); - if (os.contains("windows")) { - return is64 ? Platform.Windows64 : Platform.Windows32; - } else if (os.contains("linux") || os.contains("freebsd") || os.contains("sunos")) { - return is64 ? Platform.Linux64 : Platform.Linux32; - } else if (os.contains("mac os x") || os.contains("darwin")) { - if (arch.startsWith("ppc")) { - return is64 ? Platform.MacOSX_PPC64 : Platform.MacOSX_PPC32; - } else { - return is64 ? Platform.MacOSX64 : Platform.MacOSX32; - } - } else { - throw new UnsupportedOperationException("The specified platform: " + os + " is not supported."); - } - } - - public abstract JmeContext newContext(AppSettings settings, JmeContext.Type contextType); - - public abstract AudioRenderer newAudioRenderer(AppSettings settings); - - public abstract void initialize(AppSettings settings); -} +/* + * Copyright (c) 2009-2010 jMonkeyEngine + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are + * met: + * + * * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * + * * Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * * Neither the name of 'jMonkeyEngine' nor the names of its contributors + * may be used to endorse or promote products derived from this software + * without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED + * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR + * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR + * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, + * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, + * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR + * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF + * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING + * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ +package com.jme3.system; + +import com.jme3.asset.AssetManager; +import com.jme3.audio.AudioRenderer; +import com.jme3.input.SoftTextDialogInput; +import java.io.File; +import java.io.InputStream; +import java.net.URL; +import java.util.logging.Logger; + +/** + * + * @author Kirill Vainer, normenhansen + */ +public abstract class JmeSystemDelegate { + + protected final Logger logger = Logger.getLogger(JmeSystem.class.getName()); + protected boolean initialized = false; + protected boolean lowPermissions = false; + protected File storageFolder = null; + protected SoftTextDialogInput softTextDialogInput = null; + + public synchronized File getStorageFolder() { + if (lowPermissions) { + throw new UnsupportedOperationException("File system access restricted"); + } + if (storageFolder == null) { + // Initialize storage folder + storageFolder = new File(System.getProperty("user.home"), ".jme3"); + if (!storageFolder.exists()) { + storageFolder.mkdir(); + } + } + return storageFolder; + } + + public String getFullName() { + return JmeVersion.FULL_NAME; + } + + public InputStream getResourceAsStream(String name) { + return this.getClass().getResourceAsStream(name); + } + + public URL getResource(String name) { + return this.getClass().getResource(name); + } + + public boolean trackDirectMemory() { + return false; + } + + public void setLowPermissions(boolean lowPerm) { + lowPermissions = lowPerm; + } + + public boolean isLowPermissions() { + return lowPermissions; + } + + public void setSoftTextDialogInput(SoftTextDialogInput input) { + softTextDialogInput = input; + } + public SoftTextDialogInput getSoftTextDialogInput() { + return softTextDialogInput; + } + + public abstract AssetManager newAssetManager(URL configFile); + + public abstract AssetManager newAssetManager(); + + public abstract boolean showSettingsDialog(AppSettings sourceSettings, boolean loadFromRegistry); + + private boolean is64Bit(String arch) { + if (arch.equals("x86")) { + return false; + } else if (arch.equals("amd64")) { + return true; + } else if (arch.equals("x86_64")) { + return true; + } else if (arch.equals("ppc") || arch.equals("PowerPC")) { + return false; + } else if (arch.equals("ppc64")) { + return true; + } else if (arch.equals("i386") || arch.equals("i686")) { + return false; + } else if (arch.equals("universal")) { + return false; + } else { + throw new UnsupportedOperationException("Unsupported architecture: " + arch); + } + } + + public Platform getPlatform() { + String os = System.getProperty("os.name").toLowerCase(); + String arch = System.getProperty("os.arch").toLowerCase(); + boolean is64 = is64Bit(arch); + if (os.contains("windows")) { + return is64 ? Platform.Windows64 : Platform.Windows32; + } else if (os.contains("linux") || os.contains("freebsd") || os.contains("sunos")) { + return is64 ? Platform.Linux64 : Platform.Linux32; + } else if (os.contains("mac os x") || os.contains("darwin")) { + if (arch.startsWith("ppc")) { + return is64 ? Platform.MacOSX_PPC64 : Platform.MacOSX_PPC32; + } else { + return is64 ? Platform.MacOSX64 : Platform.MacOSX32; + } + } else { + throw new UnsupportedOperationException("The specified platform: " + os + " is not supported."); + } + } + + public abstract JmeContext newContext(AppSettings settings, JmeContext.Type contextType); + + public abstract AudioRenderer newAudioRenderer(AppSettings settings); + + public abstract void initialize(AppSettings settings); +} diff --git a/engine/src/core/com/jme3/util/BufferUtils.java b/engine/src/core/com/jme3/util/BufferUtils.java index f0cc698..2e59e97 100644 --- a/engine/src/core/com/jme3/util/BufferUtils.java +++ b/engine/src/core/com/jme3/util/BufferUtils.java
@@ -42,6 +42,7 @@ import java.util.Collections; import java.util.Map; import java.util.WeakHashMap; +import java.util.concurrent.atomic.AtomicBoolean; import java.util.logging.Level; import java.util.logging.Logger; @@ -1144,6 +1145,47 @@ } } + private static final AtomicBoolean loadedMethods = new AtomicBoolean(false); + private static Method cleanerMethod = null; + private static Method cleanMethod = null; + private static Method viewedBufferMethod = null; + private static Method freeMethod = null; + + private static Method loadMethod(String className, String methodName){ + try { + Method method = Class.forName(className).getMethod(methodName); + method.setAccessible(true); + return method; + } catch (NoSuchMethodException ex) { + return null; // the method was not found + } catch (SecurityException ex) { + return null; // setAccessible not allowed by security policy + } catch (ClassNotFoundException ex) { + return null; // the direct buffer implementation was not found + } + } + + private static void loadCleanerMethods() { + // If its already true, exit, if not, set it to true. + if (loadedMethods.getAndSet(true)) { + return; + } + // This could potentially be called many times if used from multiple + // threads + synchronized (loadedMethods) { + // Oracle JRE / OpenJDK + cleanerMethod = loadMethod("sun.nio.ch.DirectBuffer", "cleaner"); + cleanMethod = loadMethod("sun.misc.Cleaner", "clean"); + viewedBufferMethod = loadMethod("sun.nio.ch.DirectBuffer", "viewedBuffer"); + + // Apache Harmony + freeMethod = loadMethod("org.apache.harmony.nio.internal.DirectBuffer", "free"); + + // GUN Classpath (not likely) + //finalizeMethod = loadMethod("java.nio.DirectByteBufferImpl", "finalize"); + } + } + /** * Direct buffers are garbage collected by using a phantom reference and a * reference queue. Every once a while, the JVM checks the reference queue and @@ -1157,27 +1199,27 @@ * */ public static void destroyDirectBuffer(Buffer toBeDestroyed) { - if (!toBeDestroyed.isDirect()) { return; } + + loadCleanerMethods(); + try { - Method cleanerMethod = toBeDestroyed.getClass().getMethod("cleaner"); - cleanerMethod.setAccessible(true); - Object cleaner = cleanerMethod.invoke(toBeDestroyed); - if (cleaner != null) { - Method cleanMethod = cleaner.getClass().getMethod("clean"); - cleanMethod.setAccessible(true); - cleanMethod.invoke(cleaner); + if (freeMethod != null) { + freeMethod.invoke(toBeDestroyed); } else { - // Try the alternate approach of getting the viewed buffer - Method viewedBufferMethod = toBeDestroyed.getClass().getMethod("viewedBuffer"); - viewedBufferMethod.setAccessible(true); - Object viewedBuffer = viewedBufferMethod.invoke(toBeDestroyed); - if (viewedBuffer != null) { - destroyDirectBuffer( (Buffer)viewedBuffer ); + Object cleaner = cleanerMethod.invoke(toBeDestroyed); + if (cleaner != null) { + cleanMethod.invoke(cleaner); } else { - Logger.getLogger(BufferUtils.class.getName()).log(Level.SEVERE, "Buffer cannot be destroyed: {0}", toBeDestroyed); + // Try the alternate approach of getting the viewed buffer first + Object viewedBuffer = viewedBufferMethod.invoke(toBeDestroyed); + if (viewedBuffer != null) { + destroyDirectBuffer((Buffer) viewedBuffer); + } else { + Logger.getLogger(BufferUtils.class.getName()).log(Level.SEVERE, "Buffer cannot be destroyed: {0}", toBeDestroyed); + } } } } catch (IllegalAccessException ex) { @@ -1186,11 +1228,8 @@ Logger.getLogger(BufferUtils.class.getName()).log(Level.SEVERE, "{0}", ex); } catch (InvocationTargetException ex) { Logger.getLogger(BufferUtils.class.getName()).log(Level.SEVERE, "{0}", ex); - } catch (NoSuchMethodException ex) { - Logger.getLogger(BufferUtils.class.getName()).log(Level.SEVERE, "{0}", ex); } catch (SecurityException ex) { Logger.getLogger(BufferUtils.class.getName()).log(Level.SEVERE, "{0}", ex); } } - } diff --git a/engine/src/core/com/jme3/util/IntMap.java b/engine/src/core/com/jme3/util/IntMap.java index edf659b..1b91119 100644 --- a/engine/src/core/com/jme3/util/IntMap.java +++ b/engine/src/core/com/jme3/util/IntMap.java
@@ -44,8 +44,6 @@ * @author Nate */ public final class IntMap<T> implements Iterable<Entry<T>>, Cloneable { - - private final IntMapIterator iterator = new IntMapIterator(); private Entry[] table; private final float loadFactor; @@ -200,8 +198,9 @@ } public Iterator<Entry<T>> iterator() { - iterator.beginUse(); - return iterator; + IntMapIterator it = new IntMapIterator(); + it.beginUse(); + return it; } final class IntMapIterator implements Iterator<Entry<T>> { diff --git a/engine/src/core/com/jme3/util/TangentBinormalGenerator.java b/engine/src/core/com/jme3/util/TangentBinormalGenerator.java index 88f6822..8816b91 100644 --- a/engine/src/core/com/jme3/util/TangentBinormalGenerator.java +++ b/engine/src/core/com/jme3/util/TangentBinormalGenerator.java
@@ -117,7 +117,13 @@ } } else { Geometry geom = (Geometry) scene; - generate(geom.getMesh()); + Mesh mesh = geom.getMesh(); + + // Check to ensure mesh has texcoords and normals before generating + if (mesh.getBuffer(Type.TexCoord) != null + && mesh.getBuffer(Type.Normal) != null){ + generate(geom.getMesh()); + } } } @@ -640,7 +646,7 @@ lineMesh.setBuffer(Type.Color, 4, lineColor); lineMesh.setStatic(); - lineMesh.setInterleaved(); + //lineMesh.setInterleaved(); return lineMesh; } @@ -733,7 +739,7 @@ lineMesh.setBuffer(Type.Color, 4, lineColor); lineMesh.setStatic(); - lineMesh.setInterleaved(); + //lineMesh.setInterleaved(); return lineMesh; } } diff --git a/engine/src/desktop/com/jme3/texture/plugins/AWTLoader.java b/engine/src/desktop/com/jme3/texture/plugins/AWTLoader.java index 3f471f7..9513bcf 100644 --- a/engine/src/desktop/com/jme3/texture/plugins/AWTLoader.java +++ b/engine/src/desktop/com/jme3/texture/plugins/AWTLoader.java
@@ -33,6 +33,7 @@ package com.jme3.texture.plugins; import com.jme3.asset.AssetInfo; +import com.jme3.asset.AssetLoadException; import com.jme3.asset.AssetLoader; import com.jme3.asset.TextureKey; import com.jme3.texture.Image; @@ -185,27 +186,30 @@ public Image load(InputStream in, boolean flipY) throws IOException{ ImageIO.setUseCache(false); BufferedImage img = ImageIO.read(in); - if (img == null) + if (img == null){ return null; - + } return load(img, flipY); } public Object load(AssetInfo info) throws IOException { - if (ImageIO.getImageWritersBySuffix(info.getKey().getExtension()) != null){ - + if (ImageIO.getImageReadersBySuffix(info.getKey().getExtension()) != null){ boolean flip = ((TextureKey) info.getKey()).isFlipY(); InputStream in = null; try { in = info.openStream(); Image img = load(in, flip); + if (img == null){ + throw new AssetLoadException("The given image cannot be loaded " + info.getKey()); + } return img; } finally { if (in != null){ in.close(); } } + }else{ + throw new AssetLoadException("The extension " + info.getKey().getExtension() + " is not supported"); } - return null; } } diff --git a/engine/src/niftygui/Common/MatDefs/Nifty/Nifty.frag b/engine/src/niftygui/Common/MatDefs/Nifty/Nifty.frag deleted file mode 100644 index 5e77548..0000000 --- a/engine/src/niftygui/Common/MatDefs/Nifty/Nifty.frag +++ /dev/null
@@ -1,13 +0,0 @@ -uniform bool m_UseTex; -uniform sampler2D m_Texture; -uniform vec4 m_Color; - -varying vec2 texCoord; -varying vec4 color; - -void main() { - vec4 texVal = texture2D(m_Texture, texCoord); - texVal = m_UseTex ? texVal : vec4(1.0); - gl_FragColor = texVal * color * m_Color; -} - diff --git a/engine/src/niftygui/Common/MatDefs/Nifty/NiftyQuad.frag b/engine/src/niftygui/Common/MatDefs/Nifty/NiftyQuad.frag new file mode 100644 index 0000000..31f49d6 --- /dev/null +++ b/engine/src/niftygui/Common/MatDefs/Nifty/NiftyQuad.frag
@@ -0,0 +1,6 @@ +uniform vec4 m_Color; + +void main() { + gl_FragColor = m_Color; +} + diff --git a/engine/src/niftygui/Common/MatDefs/Nifty/NiftyQuad.j3md b/engine/src/niftygui/Common/MatDefs/Nifty/NiftyQuad.j3md new file mode 100644 index 0000000..d0213e9 --- /dev/null +++ b/engine/src/niftygui/Common/MatDefs/Nifty/NiftyQuad.j3md
@@ -0,0 +1,19 @@ +MaterialDef Default GUI { + + MaterialParameters { + Vector4 Color (Color) + } + + Technique { + VertexShader GLSL100: Common/MatDefs/Nifty/NiftyQuad.vert + FragmentShader GLSL100: Common/MatDefs/Nifty/NiftyQuad.frag + + WorldParameters { + WorldViewProjectionMatrix + } + } + + Technique FixedFunc { + } + +} \ No newline at end of file diff --git a/engine/src/niftygui/Common/MatDefs/Nifty/NiftyQuad.vert b/engine/src/niftygui/Common/MatDefs/Nifty/NiftyQuad.vert new file mode 100644 index 0000000..1eb1616 --- /dev/null +++ b/engine/src/niftygui/Common/MatDefs/Nifty/NiftyQuad.vert
@@ -0,0 +1,9 @@ +uniform mat4 g_WorldViewProjectionMatrix; + +attribute vec4 inPosition; + + +void main() { + vec2 pos = (g_WorldViewProjectionMatrix * inPosition).xy; + gl_Position = vec4(pos, 0.0, 1.0); +} \ No newline at end of file diff --git a/engine/src/niftygui/Common/MatDefs/Nifty/NiftyQuadGrad.frag b/engine/src/niftygui/Common/MatDefs/Nifty/NiftyQuadGrad.frag new file mode 100644 index 0000000..1f0a645 --- /dev/null +++ b/engine/src/niftygui/Common/MatDefs/Nifty/NiftyQuadGrad.frag
@@ -0,0 +1,6 @@ +varying vec4 color; + +void main() { + gl_FragColor = color; +} + diff --git a/engine/src/niftygui/Common/MatDefs/Nifty/NiftyQuadGrad.j3md b/engine/src/niftygui/Common/MatDefs/Nifty/NiftyQuadGrad.j3md new file mode 100644 index 0000000..4b3b22d --- /dev/null +++ b/engine/src/niftygui/Common/MatDefs/Nifty/NiftyQuadGrad.j3md
@@ -0,0 +1,18 @@ +MaterialDef Default GUI { + + MaterialParameters { + } + + Technique { + VertexShader GLSL100: Common/MatDefs/Nifty/NiftyQuadGrad.vert + FragmentShader GLSL100: Common/MatDefs/Nifty/NiftyQuadGrad.frag + + WorldParameters { + WorldViewProjectionMatrix + } + } + + Technique FixedFunc { + } + +} \ No newline at end of file diff --git a/engine/src/niftygui/Common/MatDefs/Nifty/Nifty.vert b/engine/src/niftygui/Common/MatDefs/Nifty/NiftyQuadGrad.vert similarity index 70% copy from engine/src/niftygui/Common/MatDefs/Nifty/Nifty.vert copy to engine/src/niftygui/Common/MatDefs/Nifty/NiftyQuadGrad.vert index 67c864d..0ecaee4 100644 --- a/engine/src/niftygui/Common/MatDefs/Nifty/Nifty.vert +++ b/engine/src/niftygui/Common/MatDefs/Nifty/NiftyQuadGrad.vert
@@ -2,15 +2,13 @@ attribute vec4 inPosition; attribute vec4 inColor; -attribute vec2 inTexCoord; +attribute vec4 inIndex; -varying vec2 texCoord; varying vec4 color; void main() { vec2 pos = (g_WorldViewProjectionMatrix * inPosition).xy; gl_Position = vec4(pos, 0.0, 1.0); - texCoord = inTexCoord; - color = inColor; + color = inIndex; } \ No newline at end of file diff --git a/engine/src/niftygui/Common/MatDefs/Nifty/NiftyTex.frag b/engine/src/niftygui/Common/MatDefs/Nifty/NiftyTex.frag new file mode 100644 index 0000000..b2b4b95 --- /dev/null +++ b/engine/src/niftygui/Common/MatDefs/Nifty/NiftyTex.frag
@@ -0,0 +1,10 @@ +uniform sampler2D m_Texture; +uniform vec4 m_Color; + +varying vec2 texCoord; + +void main() { + vec4 texVal = texture2D(m_Texture, texCoord); + gl_FragColor = texVal * m_Color ; +} + diff --git a/engine/src/niftygui/Common/MatDefs/Nifty/Nifty.j3md b/engine/src/niftygui/Common/MatDefs/Nifty/NiftyTex.j3md similarity index 62% rename from engine/src/niftygui/Common/MatDefs/Nifty/Nifty.j3md rename to engine/src/niftygui/Common/MatDefs/Nifty/NiftyTex.j3md index 9ba39b1..07f5e92 100644 --- a/engine/src/niftygui/Common/MatDefs/Nifty/Nifty.j3md +++ b/engine/src/niftygui/Common/MatDefs/Nifty/NiftyTex.j3md
@@ -2,13 +2,12 @@ MaterialParameters { Texture2D Texture - Boolean UseTex Vector4 Color (Color) } Technique { - VertexShader GLSL100: Common/MatDefs/Nifty/Nifty.vert - FragmentShader GLSL100: Common/MatDefs/Nifty/Nifty.frag + VertexShader GLSL100: Common/MatDefs/Nifty/NiftyTex.vert + FragmentShader GLSL100: Common/MatDefs/Nifty/NiftyTex.frag WorldParameters { WorldViewProjectionMatrix diff --git a/engine/src/niftygui/Common/MatDefs/Nifty/Nifty.vert b/engine/src/niftygui/Common/MatDefs/Nifty/NiftyTex.vert similarity index 72% rename from engine/src/niftygui/Common/MatDefs/Nifty/Nifty.vert rename to engine/src/niftygui/Common/MatDefs/Nifty/NiftyTex.vert index 67c864d..c5c3dc1 100644 --- a/engine/src/niftygui/Common/MatDefs/Nifty/Nifty.vert +++ b/engine/src/niftygui/Common/MatDefs/Nifty/NiftyTex.vert
@@ -1,16 +1,13 @@ uniform mat4 g_WorldViewProjectionMatrix; attribute vec4 inPosition; -attribute vec4 inColor; attribute vec2 inTexCoord; varying vec2 texCoord; -varying vec4 color; void main() { vec2 pos = (g_WorldViewProjectionMatrix * inPosition).xy; gl_Position = vec4(pos, 0.0, 1.0); - texCoord = inTexCoord; - color = inColor; + texCoord = inTexCoord; } \ No newline at end of file diff --git a/engine/src/niftygui/com/jme3/niftygui/InputSystemJme.java b/engine/src/niftygui/com/jme3/niftygui/InputSystemJme.java index 0b80131..01c2ac7 100644 --- a/engine/src/niftygui/com/jme3/niftygui/InputSystemJme.java +++ b/engine/src/niftygui/com/jme3/niftygui/InputSystemJme.java
@@ -1,233 +1,274 @@ -/* - * Copyright (c) 2009-2010 jMonkeyEngine - * All rights reserved. - * - * Redistribution and use in source and binary forms, with or without - * modification, are permitted provided that the following conditions are - * met: - * - * * Redistributions of source code must retain the above copyright - * notice, this list of conditions and the following disclaimer. - * - * * Redistributions in binary form must reproduce the above copyright - * notice, this list of conditions and the following disclaimer in the - * documentation and/or other materials provided with the distribution. - * - * * Neither the name of 'jMonkeyEngine' nor the names of its contributors - * may be used to endorse or promote products derived from this software - * without specific prior written permission. - * - * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS - * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED - * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR - * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR - * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, - * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, - * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR - * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF - * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING - * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS - * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. - */ - -package com.jme3.niftygui; - -import com.jme3.input.InputManager; -import com.jme3.input.KeyInput; -import com.jme3.input.RawInputListener; -import com.jme3.input.event.*; -import de.lessvoid.nifty.Nifty; -import de.lessvoid.nifty.NiftyInputConsumer; -import de.lessvoid.nifty.tools.resourceloader.NiftyResourceLoader; -import de.lessvoid.nifty.input.keyboard.KeyboardInputEvent; -import de.lessvoid.nifty.spi.input.InputSystem; -import java.util.ArrayList; - -public class InputSystemJme implements InputSystem, RawInputListener { - - private final ArrayList<InputEvent> inputQueue = new ArrayList<InputEvent>(); - - private InputManager inputManager; - - private boolean isDragging = false, niftyOwnsDragging = false; - private boolean pressed = false; - private int buttonIndex; - private int x, y; - private int height; - - private boolean shiftDown = false; - private boolean ctrlDown = false; - - private Nifty nifty; - - public InputSystemJme(InputManager inputManager){ - this.inputManager = inputManager; - } - - public void setResourceLoader(NiftyResourceLoader niftyResourceLoader) { - } - - public void setNifty(Nifty nifty) { - this.nifty = nifty; - } - - /** - * @param height The height of the viewport. Used to convert - * buttom-left origin to upper-left origin. - */ - public void setHeight(int height){ - this.height = height; - } - - public void setMousePosition(int x, int y){ - } - - public void beginInput(){ - } - - public void endInput(){ - boolean result = nifty.update(); - } - - private void onTouchEventQueued(TouchEvent evt, NiftyInputConsumer nic) { - boolean consumed = false; - - x = (int) evt.getX(); - y = (int) (height - evt.getY()); - - switch (evt.getType()) { - case DOWN: - consumed = nic.processMouseEvent(x, y, 0, 0, false); - isDragging = true; - niftyOwnsDragging = consumed; - if (consumed){ - evt.setConsumed(); - } - - break; - - case UP: - if (niftyOwnsDragging){ - consumed = nic.processMouseEvent(x, y, 0, buttonIndex, pressed); - if (consumed){ - evt.setConsumed(); - } - } - - isDragging = false; - niftyOwnsDragging = false; - break; - } - } - - private void onMouseMotionEventQueued(MouseMotionEvent evt, NiftyInputConsumer nic) { - x = evt.getX(); - y = height - evt.getY(); - nic.processMouseEvent(x, y, evt.getDeltaWheel(), buttonIndex, pressed); -// if (nic.processMouseEvent(niftyEvt) /*|| nifty.getCurrentScreen().isMouseOverElement()*/){ - // Do not consume motion events - //evt.setConsumed(); -// } - } - - private void onMouseButtonEventQueued(MouseButtonEvent evt, NiftyInputConsumer nic) { - boolean wasPressed = pressed; - boolean forwardToNifty = true; - - buttonIndex = evt.getButtonIndex(); - pressed = evt.isPressed(); - - // Mouse button raised. End dragging - if (wasPressed && !pressed){ - if (!niftyOwnsDragging){ - forwardToNifty = false; - } - isDragging = false; - niftyOwnsDragging = false; - } - - boolean consumed = false; - if (forwardToNifty){ - consumed = nic.processMouseEvent(x, y, 0, buttonIndex, pressed); - if (consumed){ - evt.setConsumed(); - } - } - - // Mouse button pressed. Begin dragging - if (!wasPressed && pressed){ - isDragging = true; - niftyOwnsDragging = consumed; - } - } - - private void onKeyEventQueued(KeyInputEvent evt, NiftyInputConsumer nic) { - int code = evt.getKeyCode(); - - if (code == KeyInput.KEY_LSHIFT || code == KeyInput.KEY_RSHIFT) { - shiftDown = evt.isPressed(); - } else if (code == KeyInput.KEY_LCONTROL || code == KeyInput.KEY_RCONTROL) { - ctrlDown = evt.isPressed(); - } - - KeyboardInputEvent keyEvt = new KeyboardInputEvent(code, - evt.getKeyChar(), - evt.isPressed(), - shiftDown, - ctrlDown); - - if (nic.processKeyboardEvent(keyEvt)){ - evt.setConsumed(); - } - } - - public void onMouseMotionEvent(MouseMotionEvent evt) { - // Only forward the event if there's actual motion involved. - if (inputManager.isCursorVisible() && (evt.getDX() != 0 || - evt.getDY() != 0 || - evt.getDeltaWheel() != 0)){ - inputQueue.add(evt); - } - } - - public void onMouseButtonEvent(MouseButtonEvent evt) { - if (inputManager.isCursorVisible() && evt.getButtonIndex() >= 0 && evt.getButtonIndex() <= 2){ - inputQueue.add(evt); - } - } - - public void onJoyAxisEvent(JoyAxisEvent evt) { - } - - public void onJoyButtonEvent(JoyButtonEvent evt) { - } - - public void onKeyEvent(KeyInputEvent evt) { - inputQueue.add(evt); - } - - public void onTouchEvent(TouchEvent evt) { - inputQueue.add(evt); - } - - public void forwardEvents(NiftyInputConsumer nic) { - int queueSize = inputQueue.size(); - - for (int i = 0; i < queueSize; i++){ - InputEvent evt = inputQueue.get(i); - if (evt instanceof MouseMotionEvent){ - onMouseMotionEventQueued( (MouseMotionEvent)evt, nic); - }else if (evt instanceof MouseButtonEvent){ - onMouseButtonEventQueued( (MouseButtonEvent)evt, nic); - }else if (evt instanceof KeyInputEvent){ - onKeyEventQueued( (KeyInputEvent)evt, nic); - }else if (evt instanceof TouchEvent){ - onTouchEventQueued( (TouchEvent)evt, nic); - } - } - - inputQueue.clear(); - } - - -} +/* + * Copyright (c) 2009-2010 jMonkeyEngine + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are + * met: + * + * * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * + * * Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * * Neither the name of 'jMonkeyEngine' nor the names of its contributors + * may be used to endorse or promote products derived from this software + * without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED + * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR + * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR + * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, + * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, + * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR + * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF + * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING + * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ +package com.jme3.niftygui; + +import com.jme3.input.InputManager; +import com.jme3.input.KeyInput; +import com.jme3.input.RawInputListener; +import com.jme3.input.SoftTextDialogInput; +import com.jme3.input.controls.SoftTextDialogInputListener; +import com.jme3.input.event.*; +import com.jme3.system.JmeSystem; +import de.lessvoid.nifty.Nifty; +import de.lessvoid.nifty.NiftyInputConsumer; +import de.lessvoid.nifty.controls.TextField; +import de.lessvoid.nifty.controls.nullobjects.TextFieldNull; +import de.lessvoid.nifty.elements.Element; +import de.lessvoid.nifty.tools.resourceloader.NiftyResourceLoader; +import de.lessvoid.nifty.input.keyboard.KeyboardInputEvent; +import de.lessvoid.nifty.spi.input.InputSystem; +import java.util.ArrayList; +import java.util.logging.Level; +import java.util.logging.Logger; + +public class InputSystemJme implements InputSystem, RawInputListener { + + private final ArrayList<InputEvent> inputQueue = new ArrayList<InputEvent>(); + private InputManager inputManager; + private boolean isDragging = false, niftyOwnsDragging = false; + private boolean pressed = false; + private int buttonIndex; + private int x, y; + private int height; + private boolean shiftDown = false; + private boolean ctrlDown = false; + private Nifty nifty; + + public InputSystemJme(InputManager inputManager) { + this.inputManager = inputManager; + } + + public void setResourceLoader(NiftyResourceLoader niftyResourceLoader) { + } + + public void setNifty(Nifty nifty) { + this.nifty = nifty; + } + + /** + * @param height The height of the viewport. Used to convert + * buttom-left origin to upper-left origin. + */ + public void setHeight(int height) { + this.height = height; + } + + public void setMousePosition(int x, int y) { + } + + public void beginInput() { + } + + public void endInput() { + boolean result = nifty.update(); + } + + private void onTouchEventQueued(TouchEvent evt, NiftyInputConsumer nic) { + boolean consumed = false; + + x = (int) evt.getX(); + y = (int) (height - evt.getY()); + + if (!inputManager.getSimulateMouse()) { + switch (evt.getType()) { + case DOWN: + consumed = nic.processMouseEvent(x, y, 0, 0, true); + isDragging = true; + niftyOwnsDragging = consumed; + if (consumed) { + evt.setConsumed(); + } + + break; + + case UP: + if (niftyOwnsDragging) { + consumed = nic.processMouseEvent(x, y, 0, 0, false); + if (consumed) { + evt.setConsumed(); + } + } + + isDragging = false; + niftyOwnsDragging = false; + + if (consumed) { + processSoftKeyboard(); + } + + break; + } + } + } + + private void onMouseMotionEventQueued(MouseMotionEvent evt, NiftyInputConsumer nic) { + x = evt.getX(); + y = height - evt.getY(); + nic.processMouseEvent(x, y, evt.getDeltaWheel(), buttonIndex, pressed); +// if (nic.processMouseEvent(niftyEvt) /*|| nifty.getCurrentScreen().isMouseOverElement()*/){ + // Do not consume motion events + //evt.setConsumed(); +// } + } + + private void onMouseButtonEventQueued(MouseButtonEvent evt, NiftyInputConsumer nic) { + boolean wasPressed = pressed; + boolean forwardToNifty = true; + + buttonIndex = evt.getButtonIndex(); + pressed = evt.isPressed(); + + // Mouse button raised. End dragging + if (wasPressed && !pressed) { + if (!niftyOwnsDragging) { + forwardToNifty = false; + } + isDragging = false; + niftyOwnsDragging = false; + } + + boolean consumed = false; + if (forwardToNifty) { + consumed = nic.processMouseEvent(x, y, 0, buttonIndex, pressed); + if (consumed) { + evt.setConsumed(); + } + } + + // Mouse button pressed. Begin dragging + if (!wasPressed && pressed) { + isDragging = true; + niftyOwnsDragging = consumed; + } + + if (consumed && pressed) { + processSoftKeyboard(); + } + + } + + private void onKeyEventQueued(KeyInputEvent evt, NiftyInputConsumer nic) { + int code = evt.getKeyCode(); + + if (code == KeyInput.KEY_LSHIFT || code == KeyInput.KEY_RSHIFT) { + shiftDown = evt.isPressed(); + } else if (code == KeyInput.KEY_LCONTROL || code == KeyInput.KEY_RCONTROL) { + ctrlDown = evt.isPressed(); + } + + KeyboardInputEvent keyEvt = new KeyboardInputEvent(code, + evt.getKeyChar(), + evt.isPressed(), + shiftDown, + ctrlDown); + + if (nic.processKeyboardEvent(keyEvt)) { + evt.setConsumed(); + } + } + + public void onMouseMotionEvent(MouseMotionEvent evt) { + // Only forward the event if there's actual motion involved. + if (inputManager.isCursorVisible() && (evt.getDX() != 0 + || evt.getDY() != 0 + || evt.getDeltaWheel() != 0)) { + inputQueue.add(evt); + } + } + + public void onMouseButtonEvent(MouseButtonEvent evt) { + if (inputManager.isCursorVisible() && evt.getButtonIndex() >= 0 && evt.getButtonIndex() <= 2) { + inputQueue.add(evt); + } + } + + public void onJoyAxisEvent(JoyAxisEvent evt) { + } + + public void onJoyButtonEvent(JoyButtonEvent evt) { + } + + public void onKeyEvent(KeyInputEvent evt) { + inputQueue.add(evt); + } + + public void onTouchEvent(TouchEvent evt) { + inputQueue.add(evt); + } + + public void forwardEvents(NiftyInputConsumer nic) { + int queueSize = inputQueue.size(); + + for (int i = 0; i < queueSize; i++) { + InputEvent evt = inputQueue.get(i); + if (evt instanceof MouseMotionEvent) { + onMouseMotionEventQueued((MouseMotionEvent) evt, nic); + } else if (evt instanceof MouseButtonEvent) { + onMouseButtonEventQueued((MouseButtonEvent) evt, nic); + } else if (evt instanceof KeyInputEvent) { + onKeyEventQueued((KeyInputEvent) evt, nic); + } else if (evt instanceof TouchEvent) { + onTouchEventQueued((TouchEvent) evt, nic); + } + } + + inputQueue.clear(); + } + + private void processSoftKeyboard() { + SoftTextDialogInput softTextDialogInput = JmeSystem.getSoftTextDialogInput(); + if (softTextDialogInput != null) { + + Element element = nifty.getCurrentScreen().getFocusHandler().getKeyboardFocusElement(); + if (element != null) { + final TextField textField = element.getNiftyControl(TextField.class); + if (textField != null && !(textField instanceof TextFieldNull)) { + Logger.getLogger(InputSystemJme.class.getName()).log(Level.INFO, "Current TextField: {0}", textField.getId()); + String initialValue = textField.getText(); + if (initialValue == null) { + initialValue = ""; + } + + softTextDialogInput.requestDialog(SoftTextDialogInput.TEXT_ENTRY_DIALOG, "Enter Text", initialValue, new SoftTextDialogInputListener() { + + public void onSoftText(int action, String text) { + if (action == SoftTextDialogInputListener.COMPLETE) { + textField.setText(text); + } + } + }); + } + } + } + + } +} diff --git a/engine/src/niftygui/com/jme3/niftygui/RenderDeviceJme.java b/engine/src/niftygui/com/jme3/niftygui/RenderDeviceJme.java index 8977ed6..1f36a97 100644 --- a/engine/src/niftygui/com/jme3/niftygui/RenderDeviceJme.java +++ b/engine/src/niftygui/com/jme3/niftygui/RenderDeviceJme.java
@@ -29,7 +29,6 @@ * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */ - package com.jme3.niftygui; import com.jme3.font.BitmapText; @@ -60,140 +59,148 @@ import java.util.HashMap; public class RenderDeviceJme implements RenderDevice { - + private NiftyJmeDisplay display; private RenderManager rm; private Renderer r; - private HashMap<String, BitmapText> textCacheLastFrame = new HashMap<String, BitmapText>(); private HashMap<String, BitmapText> textCacheCurrentFrame = new HashMap<String, BitmapText>(); - private final Quad quad = new Quad(1, -1, true); private final Geometry quadGeom = new Geometry("nifty-quad", quad); private final Material niftyMat; - + private final Material niftyQuadMat; + private final Material niftyQuadGradMat; private boolean clipWasSet = false; private BlendMode blendMode = null; - private VertexBuffer quadDefaultTC = quad.getBuffer(Type.TexCoord); private VertexBuffer quadModTC = quadDefaultTC.clone(); private VertexBuffer quadColor; - private Matrix4f tempMat = new Matrix4f(); private ColorRGBA tempColor = new ColorRGBA(); - - public RenderDeviceJme(NiftyJmeDisplay display){ + + public RenderDeviceJme(NiftyJmeDisplay display) { this.display = display; - + quadColor = new VertexBuffer(Type.Color); quadColor.setNormalized(true); ByteBuffer bb = BufferUtils.createByteBuffer(4 * 4); quadColor.setupData(Usage.Stream, 4, Format.UnsignedByte, bb); quad.setBuffer(quadColor); - + quadModTC.setUsage(Usage.Stream); - - niftyMat = new Material(display.getAssetManager(), "Common/MatDefs/Nifty/Nifty.j3md"); + + //Color + texture color material for text and images + niftyMat = new Material(display.getAssetManager(), "Common/MatDefs/Nifty/NiftyTex.j3md"); niftyMat.getAdditionalRenderState().setDepthTest(false); + //Color material for uniform colored quads + niftyQuadMat = new Material(display.getAssetManager(), "Common/MatDefs/Nifty/NiftyQuad.j3md"); + niftyQuadMat.getAdditionalRenderState().setDepthTest(false); + + //vertex color only for gradient quads (although i didn't find a way in nifty to make a gradient using vertex color) + niftyQuadGradMat = new Material(display.getAssetManager(), "Common/MatDefs/Nifty/NiftyQuadGrad.j3md"); + niftyQuadGradMat.getAdditionalRenderState().setDepthTest(false); + } - + public void setResourceLoader(NiftyResourceLoader niftyResourceLoader) { } - - public void setRenderManager(RenderManager rm){ + + public void setRenderManager(RenderManager rm) { this.rm = rm; this.r = rm.getRenderer(); } // TODO: Cursor support - public MouseCursor createMouseCursor(String str, int x, int y){ + public MouseCursor createMouseCursor(String str, int x, int y) { return new MouseCursor() { + public void dispose() { } }; } - - public void enableMouseCursor(MouseCursor cursor){ + + public void enableMouseCursor(MouseCursor cursor) { } - - public void disableMouseCursor(){ + + public void disableMouseCursor() { } - + public RenderImage createImage(String filename, boolean linear) { return new RenderImageJme(filename, linear, display); } - + public RenderFont createFont(String filename) { return new RenderFontJme(filename, display); } - + public void beginFrame() { } - + public void endFrame() { HashMap<String, BitmapText> temp = textCacheLastFrame; textCacheLastFrame = textCacheCurrentFrame; textCacheCurrentFrame = temp; textCacheCurrentFrame.clear(); - + // System.exit(1); } - + public int getWidth() { return display.getWidth(); } - + public int getHeight() { return display.getHeight(); } - + public void clear() { } - + public void setBlendMode(BlendMode blendMode) { - if (this.blendMode != blendMode){ + if (this.blendMode != blendMode) { this.blendMode = blendMode; } } - - private RenderState.BlendMode convertBlend(){ - if (blendMode == null) + + private RenderState.BlendMode convertBlend() { + if (blendMode == null) { return RenderState.BlendMode.Off; - else if (blendMode == BlendMode.BLEND) + } else if (blendMode == BlendMode.BLEND) { return RenderState.BlendMode.Alpha; - else if (blendMode == BlendMode.MULIPLY) + } else if (blendMode == BlendMode.MULIPLY) { return RenderState.BlendMode.Modulate; - else + } else { throw new UnsupportedOperationException(); - } - - private int convertColor(Color color){ - int color2 = 0; - color2 |= ((int)(255.0 * color.getAlpha())) << 24; - color2 |= ((int)(255.0 * color.getBlue())) << 16; - color2 |= ((int)(255.0 * color.getGreen())) << 8; - color2 |= ((int)(255.0 * color.getRed())); - return color2; - } - - private ColorRGBA convertColor(Color inColor, ColorRGBA outColor){ - return outColor.set(inColor.getRed(), inColor.getGreen(), inColor.getBlue(), inColor.getAlpha()); - } - - private void setColor(Color color){ - ByteBuffer buf = (ByteBuffer) quadColor.getData(); - buf.rewind(); - - int color2 = convertColor(color); - buf.putInt(color2); - buf.putInt(color2); - buf.putInt(color2); - buf.putInt(color2); - - buf.flip(); - quadColor.updateData(buf); + } } + private int convertColor(Color color) { + int color2 = 0; + color2 |= ((int) (255.0 * color.getAlpha())) << 24; + color2 |= ((int) (255.0 * color.getBlue())) << 16; + color2 |= ((int) (255.0 * color.getGreen())) << 8; + color2 |= ((int) (255.0 * color.getRed())); + return color2; + } + + private ColorRGBA convertColor(Color inColor, ColorRGBA outColor) { + return outColor.set(inColor.getRed(), inColor.getGreen(), inColor.getBlue(), inColor.getAlpha()); + } + +// private void setColor(Color color) { +// ByteBuffer buf = (ByteBuffer) quadColor.getData(); +// buf.rewind(); +// +// int color2 = convertColor(color); +// buf.putInt(color2); +// buf.putInt(color2); +// buf.putInt(color2); +// buf.putInt(color2); +// +// buf.flip(); +// quadColor.updateData(buf); +// } + /** * * @param font @@ -205,22 +212,23 @@ * @deprecated use renderFont(RenderFont font, String str, int x, int y, Color color, float sizeX, float sizeY) instead */ @Deprecated - public void renderFont(RenderFont font, String str, int x, int y, Color color, float size){ + public void renderFont(RenderFont font, String str, int x, int y, Color color, float size) { renderFont(font, str, x, y, color, size, size); } - + @Override - public void renderFont(RenderFont font, String str, int x, int y, Color color, float sizeX, float sizeY){ - //TODO find out what the f1 param is for - if (str.length() == 0) + public void renderFont(RenderFont font, String str, int x, int y, Color color, float sizeX, float sizeY) { + if (str.length() == 0) { return; - - if (font instanceof RenderFontNull) + } + + if (font instanceof RenderFontNull) { return; - + } + RenderFontJme jmeFont = (RenderFontJme) font; - String key = font+str+color.getColorString(); + String key = font + str + color.getColorString(); BitmapText text = textCacheLastFrame.get(key); if (text == null) { text = jmeFont.createText(); @@ -228,166 +236,149 @@ text.updateLogicalState(0); } textCacheCurrentFrame.put(key, text); - - niftyMat.setColor("Color", convertColor(color, tempColor)); - niftyMat.setBoolean("UseTex", true); + + niftyMat.setColor("Color", convertColor(color, tempColor)); niftyMat.getAdditionalRenderState().setBlendMode(RenderState.BlendMode.Alpha); // niftyMat.getAdditionalRenderState().setBlendMode(convertBlend()); text.setMaterial(niftyMat); - - float width = text.getLineWidth(); - float height = text.getLineHeight(); - - float x0 = x + 0.5f * width * (1f - sizeX); - float y0 = y + 0.5f * height * (1f - sizeY); - + tempMat.loadIdentity(); - tempMat.setTranslation(x0, getHeight() - y0, 0); + tempMat.setTranslation(x, getHeight() - y, 0); tempMat.setScale(sizeX, sizeY, 0); - + rm.setWorldMatrix(tempMat); text.render(rm); - + // System.out.println("renderFont"); } - + public void renderImage(RenderImage image, int x, int y, int w, int h, - int srcX, int srcY, int srcW, int srcH, - Color color, float scale, - int centerX, int centerY){ + int srcX, int srcY, int srcW, int srcH, + Color color, float scale, + int centerX, int centerY) { RenderImageJme jmeImage = (RenderImageJme) image; Texture2D texture = jmeImage.getTexture(); - + niftyMat.getAdditionalRenderState().setBlendMode(convertBlend()); - niftyMat.setColor("Color", ColorRGBA.White); - niftyMat.setTexture("Texture", texture); - niftyMat.setBoolean("UseTex", true); - setColor(color); - - float imageWidth = jmeImage.getWidth(); + niftyMat.setColor("Color", convertColor(color, tempColor)); + niftyMat.setTexture("Texture", texture); + //setColor(color); + + float imageWidth = jmeImage.getWidth(); float imageHeight = jmeImage.getHeight(); FloatBuffer texCoords = (FloatBuffer) quadModTC.getData(); - + float startX = srcX / imageWidth; float startY = srcY / imageHeight; - float endX = startX + (srcW / imageWidth); - float endY = startY + (srcH / imageHeight); - + float endX = startX + (srcW / imageWidth); + float endY = startY + (srcH / imageHeight); + startY = 1f - startY; - endY = 1f - endY; - + endY = 1f - endY; + texCoords.rewind(); texCoords.put(startX).put(startY); - texCoords.put(endX) .put(startY); - texCoords.put(endX) .put(endY); + texCoords.put(endX).put(startY); + texCoords.put(endX).put(endY); texCoords.put(startX).put(endY); texCoords.flip(); quadModTC.updateData(texCoords); - + quad.clearBuffer(Type.TexCoord); quad.setBuffer(quadModTC); - + float x0 = centerX + (x - centerX) * scale; float y0 = centerY + (y - centerY) * scale; - + tempMat.loadIdentity(); tempMat.setTranslation(x0, getHeight() - y0, 0); tempMat.setScale(w * scale, h * scale, 0); - + rm.setWorldMatrix(tempMat); niftyMat.render(quadGeom, rm); // // System.out.println("renderImage (Sub)"); } - + public void renderImage(RenderImage image, int x, int y, int width, int height, - Color color, float imageScale){ - + Color color, float imageScale) { + RenderImageJme jmeImage = (RenderImageJme) image; - + niftyMat.getAdditionalRenderState().setBlendMode(convertBlend()); - niftyMat.setColor("Color", ColorRGBA.White); - niftyMat.setTexture("Texture", jmeImage.getTexture()); - niftyMat.setBoolean("UseTex", true); - setColor(color); - + niftyMat.setColor("Color", convertColor(color, tempColor)); + niftyMat.setTexture("Texture", jmeImage.getTexture()); + //setColor(color); + quad.clearBuffer(Type.TexCoord); quad.setBuffer(quadDefaultTC); - - float x0 = x + 0.5f * width * (1f - imageScale); + + float x0 = x + 0.5f * width * (1f - imageScale); float y0 = y + 0.5f * height * (1f - imageScale); - + tempMat.loadIdentity(); tempMat.setTranslation(x0, getHeight() - y0, 0); tempMat.setScale(width * imageScale, height * imageScale, 0); - + rm.setWorldMatrix(tempMat); niftyMat.render(quadGeom, rm); // // System.out.println("renderImage"); } - - public void renderQuad(int x, int y, int width, int height, Color color){ - niftyMat.getAdditionalRenderState().setBlendMode(convertBlend()); - niftyMat.setColor("Color", ColorRGBA.White); - niftyMat.clearParam("Texture"); - niftyMat.setBoolean("UseTex", false); - setColor(color); - - tempMat.loadIdentity(); - tempMat.setTranslation(x, getHeight() - y, 0); - tempMat.setScale(width, height, 0); - - rm.setWorldMatrix(tempMat); - niftyMat.render(quadGeom, rm); - + + public void renderQuad(int x, int y, int width, int height, Color color) { + if (color.getAlpha() > 0) { + niftyQuadMat.getAdditionalRenderState().setBlendMode(convertBlend()); + niftyQuadMat.setColor("Color", convertColor(color, tempColor)); + + tempMat.loadIdentity(); + tempMat.setTranslation(x, getHeight() - y, 0); + tempMat.setScale(width, height, 0); + + rm.setWorldMatrix(tempMat); + niftyQuadMat.render(quadGeom, rm); + } // System.out.println("renderQuad (Solid)"); } - + public void renderQuad(int x, int y, int width, int height, - Color topLeft, Color topRight, Color bottomRight, Color bottomLeft) { + Color topLeft, Color topRight, Color bottomRight, Color bottomLeft) { ByteBuffer buf = (ByteBuffer) quadColor.getData(); buf.rewind(); buf.putInt(convertColor(topRight)); buf.putInt(convertColor(topLeft)); - + buf.putInt(convertColor(bottomLeft)); buf.putInt(convertColor(bottomRight)); buf.flip(); quadColor.updateData(buf); - niftyMat.getAdditionalRenderState().setBlendMode(convertBlend()); - niftyMat.setColor("Color", ColorRGBA.White); - niftyMat.clearParam("Texture"); - niftyMat.setBoolean("UseTex", false); - + niftyQuadGradMat.getAdditionalRenderState().setBlendMode(convertBlend()); + tempMat.loadIdentity(); tempMat.setTranslation(x, getHeight() - y, 0); tempMat.setScale(width, height, 0); - + rm.setWorldMatrix(tempMat); - niftyMat.render(quadGeom, rm); + niftyQuadGradMat.render(quadGeom, rm); // // System.out.println("renderQuad (Grad)"); } - - public void enableClip(int x0, int y0, int x1, int y1){ + + public void enableClip(int x0, int y0, int x1, int y1) { // System.out.println("enableClip"); clipWasSet = true; r.setClipRect(x0, getHeight() - y1, x1 - x0, y1 - y0); } - + public void disableClip() { // System.out.println("disableClip"); - if (clipWasSet){ + if (clipWasSet) { r.clearClipRect(); clipWasSet = false; } } - - - } diff --git a/engine/src/ogre/com/jme3/scene/plugins/ogre/MeshLoader.java b/engine/src/ogre/com/jme3/scene/plugins/ogre/MeshLoader.java index e7f4b10..fc8cc6b 100644 --- a/engine/src/ogre/com/jme3/scene/plugins/ogre/MeshLoader.java +++ b/engine/src/ogre/com/jme3/scene/plugins/ogre/MeshLoader.java
@@ -712,9 +712,8 @@ } else if (qName.equals("geometry") || qName.equals("sharedgeometry")) { // finish writing to buffers - IntMap<VertexBuffer> bufs = mesh.getBuffers(); - for (Entry<VertexBuffer> entry : bufs) { - Buffer data = entry.getValue().getData(); + for (VertexBuffer buf : mesh.getBufferList().getArray()) { + Buffer data = buf.getData(); if (data.position() != 0) { data.flip(); } diff --git a/engine/src/terrain/com/jme3/terrain/geomipmap/LODGeomap.java b/engine/src/terrain/com/jme3/terrain/geomipmap/LODGeomap.java index 3944b98..3348b48 100644 --- a/engine/src/terrain/com/jme3/terrain/geomipmap/LODGeomap.java +++ b/engine/src/terrain/com/jme3/terrain/geomipmap/LODGeomap.java
@@ -34,6 +34,7 @@ import com.jme3.export.JmeExporter; import com.jme3.export.JmeImporter; import com.jme3.math.FastMath; +import com.jme3.math.Plane; import com.jme3.math.Triangle; import com.jme3.math.Vector2f; import com.jme3.math.Vector3f; @@ -87,7 +88,7 @@ IntBuffer ib = writeIndexArrayLodDiff(null, lod, rightLod, topLod, leftLod, bottomLod); FloatBuffer bb = BufferUtils.createFloatBuffer(getWidth() * getHeight() * 3); FloatBuffer tanb = BufferUtils.createFloatBuffer(getWidth() * getHeight() * 3); - writeTangentArray(tanb, bb, texb, scale); + writeTangentArray(nb, tanb, bb, texb, scale); Mesh m = new Mesh(); m.setMode(Mode.TriangleStrip); m.setBuffer(Type.Position, 3, pb); @@ -627,7 +628,7 @@ return num; } - public FloatBuffer[] writeTangentArray(FloatBuffer tangentStore, FloatBuffer binormalStore, FloatBuffer textureBuffer, Vector3f scale) { + public FloatBuffer[] writeTangentArray(FloatBuffer normalBuffer, FloatBuffer tangentStore, FloatBuffer binormalStore, FloatBuffer textureBuffer, Vector3f scale) { if (!isLoaded()) { throw new NullPointerException(); } @@ -650,19 +651,31 @@ } binormalStore.rewind(); + Vector3f normal = new Vector3f(); Vector3f tangent = new Vector3f(); Vector3f binormal = new Vector3f(); - Vector3f v1 = new Vector3f(); + /*Vector3f v1 = new Vector3f(); Vector3f v2 = new Vector3f(); Vector3f v3 = new Vector3f(); Vector2f t1 = new Vector2f(); Vector2f t2 = new Vector2f(); - Vector2f t3 = new Vector2f(); - - //scale = Vector3f.UNIT_XYZ; + Vector2f t3 = new Vector2f();*/ for (int r = 0; r < getHeight(); r++) { for (int c = 0; c < getWidth(); c++) { + + int idx = (r * getWidth() + c) * 3; + normal.set(normalBuffer.get(idx), normalBuffer.get(idx+1), normalBuffer.get(idx+2)); + tangent.set(normal.cross(new Vector3f(0,0,1))); + binormal.set(new Vector3f(1,0,0).cross(normal)); + + BufferUtils.setInBuffer(tangent.normalizeLocal(), tangentStore, (r * getWidth() + c)); // save the tangent + BufferUtils.setInBuffer(binormal.normalizeLocal(), binormalStore, (r * getWidth() + c)); // save the binormal + } + } + +/* for (int r = 0; r < getHeight(); r++) { + for (int c = 0; c < getWidth(); c++) { int texIdx = ((getHeight() - 1 - r) * getWidth() + c) * 2; // pull from the end int texIdxAbove = ((getHeight() - 1 - (r - 1)) * getWidth() + c) * 2; // pull from the end @@ -702,7 +715,7 @@ BufferUtils.setInBuffer(binormal, binormalStore, (r * getWidth() + c)); // save the binormal } } - + */ return new FloatBuffer[]{tangentStore, binormalStore}; } diff --git a/engine/src/terrain/com/jme3/terrain/geomipmap/TerrainPatch.java b/engine/src/terrain/com/jme3/terrain/geomipmap/TerrainPatch.java index 40cf190..83537af 100644 --- a/engine/src/terrain/com/jme3/terrain/geomipmap/TerrainPatch.java +++ b/engine/src/terrain/com/jme3/terrain/geomipmap/TerrainPatch.java
@@ -314,13 +314,25 @@ getMesh().getBuffer(Type.Normal).updateData(newNormalBuffer); FloatBuffer newTangentBuffer = null; FloatBuffer newBinormalBuffer = null; - FloatBuffer[] tb = geomap.writeTangentArray(newTangentBuffer, newBinormalBuffer, (FloatBuffer)getMesh().getBuffer(Type.TexCoord).getData(), getWorldScale()); + FloatBuffer[] tb = geomap.writeTangentArray(newNormalBuffer, newTangentBuffer, newBinormalBuffer, (FloatBuffer)getMesh().getBuffer(Type.TexCoord).getData(), getWorldScale()); newTangentBuffer = tb[0]; newBinormalBuffer = tb[1]; getMesh().getBuffer(Type.Tangent).updateData(newTangentBuffer); getMesh().getBuffer(Type.Binormal).updateData(newBinormalBuffer); } + private void setInBuffer(Mesh mesh, int index, Vector3f normal, Vector3f tangent, Vector3f binormal) { + VertexBuffer NB = mesh.getBuffer(Type.Normal); + VertexBuffer TB = mesh.getBuffer(Type.Tangent); + VertexBuffer BB = mesh.getBuffer(Type.Binormal); + BufferUtils.setInBuffer(normal, (FloatBuffer)NB.getData(), index); + BufferUtils.setInBuffer(tangent, (FloatBuffer)TB.getData(), index); + BufferUtils.setInBuffer(binormal, (FloatBuffer)BB.getData(), index); + NB.setUpdateNeeded(); + TB.setUpdateNeeded(); + BB.setUpdateNeeded(); + } + /** * Matches the normals along the edge of the patch with the neighbours. * Computes the normals for the right, bottom, left, and top edges of the @@ -357,158 +369,122 @@ Vector3f binormal = new Vector3f(); Vector3f normal = new Vector3f(); + int s = this.getSize()-1; if (right != null) { // right side, works its way down for (int i=0; i<s+1; i++) { - rootPoint.set(s, this.getHeightmapHeight(s,i), i); - leftPoint.set(s-1, this.getHeightmapHeight(s-1,i), i); - rightPoint.set(s+1, right.getHeightmapHeight(1,i), i); + rootPoint.set(0, this.getHeightmapHeight(s,i), 0); + leftPoint.set(-1, this.getHeightmapHeight(s-1,i), 0); + rightPoint.set(1, right.getHeightmapHeight(1,i), 0); if (i == 0) { // top point + bottomPoint.set(0, this.getHeightmapHeight(s,i+1), 1); + if (top == null) { - bottomPoint.set(s, this.getHeightmapHeight(s,i+1), i+1); - - averageNormalsTangents(null, rootPoint, leftPoint, bottomPoint, rightPoint, null, null, null, null, null, normal, tangent, binormal); - VertexBuffer tpNB = this.getMesh().getBuffer(Type.Normal); - VertexBuffer rightNB = right.getMesh().getBuffer(Type.Normal); - BufferUtils.setInBuffer(normal, (FloatBuffer)tpNB.getData(), s); - BufferUtils.setInBuffer(normal, (FloatBuffer)rightNB.getData(), 0); + averageNormalsTangents(null, rootPoint, leftPoint, bottomPoint, rightPoint, normal, tangent, binormal); + setInBuffer(this.getMesh(), s, normal, tangent, binormal); + setInBuffer(right.getMesh(), 0, normal, tangent, binormal); } else { - topPoint.set(s, top.getHeightmapHeight(s,s-1), i-1); - bottomPoint.set(s, this.getHeightmapHeight(s,i+1), i+1); + topPoint.set(0, top.getHeightmapHeight(s,s-1), -1); - averageNormalsTangents(topPoint, rootPoint, leftPoint, bottomPoint, rightPoint, null, null, null, null, null, normal, tangent, binormal); - VertexBuffer tpNB = this.getMesh().getBuffer(Type.Normal); - VertexBuffer rightNB = right.getMesh().getBuffer(Type.Normal); - BufferUtils.setInBuffer(normal, (FloatBuffer)tpNB.getData(), s); - BufferUtils.setInBuffer(normal, (FloatBuffer)rightNB.getData(), 0); + averageNormalsTangents(topPoint, rootPoint, leftPoint, bottomPoint, rightPoint,normal, tangent, binormal); + setInBuffer(this.getMesh(), s, normal, tangent, binormal); + setInBuffer(right.getMesh(), 0, normal, tangent, binormal); + setInBuffer(top.getMesh(), (s+1)*(s+1)-1, normal, tangent, binormal); if (topRight != null) { - VertexBuffer topRightNB = topRight.getMesh().getBuffer(Type.Normal); - BufferUtils.setInBuffer(normal, (FloatBuffer)topRightNB.getData(), (s+1)*s); - topRightNB.setUpdateNeeded(); + // setInBuffer(topRight.getMesh(), (s+1)*s, normal, tangent, binormal); } } } else if (i == s) { // bottom point + topPoint.set(0, this.getHeightmapHeight(s,s-1), -1); + if (bottom == null) { - topPoint.set(s, this.getHeightmapHeight(s,i-1), i-1); - - averageNormalsTangents(topPoint, rootPoint, leftPoint, null, rightPoint, null, null, null, null, null, normal, tangent, binormal); - VertexBuffer tpNB = this.getMesh().getBuffer(Type.Normal); - VertexBuffer rightNB = right.getMesh().getBuffer(Type.Normal); - BufferUtils.setInBuffer(normal, (FloatBuffer)tpNB.getData(), (s+1)*(i+1)-1); - BufferUtils.setInBuffer(normal, (FloatBuffer)rightNB.getData(), (s+1)*(s)); + averageNormalsTangents(topPoint, rootPoint, leftPoint, null, rightPoint, normal, tangent, binormal); + setInBuffer(this.getMesh(), (s+1)*(s+1)-1, normal, tangent, binormal); + setInBuffer(right.getMesh(), (s+1)*(s), normal, tangent, binormal); } else { - topPoint.set(s, this.getHeightmapHeight(s,i-1), i-1); - bottomPoint.set(s, bottom.getHeightmapHeight(s,1), i+1); - averageNormalsTangents(topPoint, rootPoint, leftPoint, bottomPoint, rightPoint, null, null, null, null, null, normal, tangent, binormal); - VertexBuffer tpNB = this.getMesh().getBuffer(Type.Normal); - VertexBuffer rightNB = right.getMesh().getBuffer(Type.Normal); - VertexBuffer downNB = bottom.getMesh().getBuffer(Type.Normal); - - BufferUtils.setInBuffer(normal, (FloatBuffer)tpNB.getData(), (s+1)*(s+1)-1); - BufferUtils.setInBuffer(normal, (FloatBuffer)rightNB.getData(), (s+1)*s); - BufferUtils.setInBuffer(normal, (FloatBuffer)downNB.getData(), s); + bottomPoint.set(0, bottom.getHeightmapHeight(s,1), 1); + averageNormalsTangents(topPoint, rootPoint, leftPoint, bottomPoint, rightPoint, normal, tangent, binormal); + setInBuffer(this.getMesh(), (s+1)*(s+1)-1, normal, tangent, binormal); + setInBuffer(right.getMesh(), (s+1)*s, normal, tangent, binormal); + setInBuffer(bottom.getMesh(), s, normal, tangent, binormal); if (bottomRight != null) { - VertexBuffer bottomRightNB = bottomRight.getMesh().getBuffer(Type.Normal); - BufferUtils.setInBuffer(normal, (FloatBuffer)bottomRightNB.getData(), 0); - bottomRightNB.setUpdateNeeded(); + // setInBuffer(bottomRight.getMesh(), 0, normal, tangent, binormal); } - downNB.setUpdateNeeded(); } } else { // all in the middle - topPoint.set(s, this.getHeightmapHeight(s,i-1), i-1); - bottomPoint.set(s, this.getHeightmapHeight(s,i+1), i+1); - averageNormalsTangents(topPoint, rootPoint, leftPoint, bottomPoint, rightPoint, null, null, null, null, null, normal, tangent, binormal); - VertexBuffer tpNB = this.getMesh().getBuffer(Type.Normal); - VertexBuffer rightNB = right.getMesh().getBuffer(Type.Normal); - BufferUtils.setInBuffer(normal, (FloatBuffer)tpNB.getData(), (s+1)*(i+1)-1); - BufferUtils.setInBuffer(normal, (FloatBuffer)rightNB.getData(), (s+1)*(i)); + topPoint.set(0, this.getHeightmapHeight(s,i-1), -1); + bottomPoint.set(0, this.getHeightmapHeight(s,i+1), 1); + averageNormalsTangents(topPoint, rootPoint, leftPoint, bottomPoint, rightPoint, normal, tangent, binormal); + setInBuffer(this.getMesh(), (s+1)*(i+1)-1, normal, tangent, binormal); + setInBuffer(right.getMesh(), (s+1)*(i), normal, tangent, binormal); } } - right.getMesh().getBuffer(Type.Normal).setUpdateNeeded(); } if (left != null) { // left side, works its way down for (int i=0; i<s+1; i++) { - rootPoint.set(0, this.getHeightmapHeight(0,i), i); - leftPoint.set(-1, left.getHeightmapHeight(s-1,i), i); - rightPoint.set(1, this.getHeightmapHeight(1,i), i); + rootPoint.set(0, this.getHeightmapHeight(0,i), 0); + leftPoint.set(-1, left.getHeightmapHeight(s-1,i), 0); + rightPoint.set(1, this.getHeightmapHeight(1,i), 0); if (i == 0) { // top point + bottomPoint.set(0, this.getHeightmapHeight(0,i+1), 1); + if (top == null) { - bottomPoint.set(0, this.getHeightmapHeight(0,i+1), i+1); - averageNormalsTangents(null, rootPoint, leftPoint, bottomPoint, rightPoint, null, null, null, null, null, normal, tangent, binormal); - VertexBuffer tpNB = this.getMesh().getBuffer(Type.Normal); - VertexBuffer leftNB = left.getMesh().getBuffer(Type.Normal); - BufferUtils.setInBuffer(normal, (FloatBuffer)tpNB.getData(), 0); - BufferUtils.setInBuffer(normal, (FloatBuffer)leftNB.getData(), s); + averageNormalsTangents(null, rootPoint, leftPoint, bottomPoint, rightPoint, normal, tangent, binormal); + setInBuffer(this.getMesh(), 0, normal, tangent, binormal); + setInBuffer(left.getMesh(), s, normal, tangent, binormal); } else { - topPoint.set(0, top.getHeightmapHeight(0,s-1), i-1); - bottomPoint.set(0, this.getHeightmapHeight(0,i+1), i+1); + topPoint.set(0, top.getHeightmapHeight(0,s-1), -1); - averageNormalsTangents(topPoint, rootPoint, leftPoint, bottomPoint, rightPoint, null, null, null, null, null, normal, tangent, binormal); - VertexBuffer tpNB = this.getMesh().getBuffer(Type.Normal); - VertexBuffer leftNB = left.getMesh().getBuffer(Type.Normal); - BufferUtils.setInBuffer(normal, (FloatBuffer)tpNB.getData(), 0); - BufferUtils.setInBuffer(normal, (FloatBuffer)leftNB.getData(), s); + averageNormalsTangents(topPoint, rootPoint, leftPoint, bottomPoint, rightPoint, normal, tangent, binormal); + setInBuffer(this.getMesh(), 0, normal, tangent, binormal); + setInBuffer(left.getMesh(), s, normal, tangent, binormal); + setInBuffer(top.getMesh(), (s+1)*s, normal, tangent, binormal); if (topLeft != null) { - VertexBuffer topLeftNB = topLeft.getMesh().getBuffer(Type.Normal); - BufferUtils.setInBuffer(normal, (FloatBuffer)topLeftNB.getData(), (s+1)*(s+1)-1); - topLeftNB.setUpdateNeeded(); + // setInBuffer(topLeft.getMesh(), (s+1)*(s+1)-1, normal, tangent, binormal); } } } else if (i == s) { // bottom point + topPoint.set(0, this.getHeightmapHeight(0,i-1), -1); + if (bottom == null) { - topPoint.set(0, this.getHeightmapHeight(0,i-1), i-1); - - averageNormalsTangents(topPoint, rootPoint, leftPoint, null, rightPoint, null, null, null, null, null, normal, tangent, binormal); - VertexBuffer tpNB = this.getMesh().getBuffer(Type.Normal); - VertexBuffer leftNB = left.getMesh().getBuffer(Type.Normal); - BufferUtils.setInBuffer(normal, (FloatBuffer)tpNB.getData(), (s+1)*(s)); - BufferUtils.setInBuffer(normal, (FloatBuffer)leftNB.getData(), (s+1)*(i+1)-1); + averageNormalsTangents(topPoint, rootPoint, leftPoint, null, rightPoint, normal, tangent, binormal); + setInBuffer(this.getMesh(), (s+1)*(s), normal, tangent, binormal); + setInBuffer(left.getMesh(), (s+1)*(s+1)-1, normal, tangent, binormal); } else { - topPoint.set(0, this.getHeightmapHeight(0,i-1), i-1); - bottomPoint.set(0, bottom.getHeightmapHeight(0,1), i+1); + bottomPoint.set(0, bottom.getHeightmapHeight(0,1), 1); - averageNormalsTangents(topPoint, rootPoint, leftPoint, bottomPoint, rightPoint, null, null, null, null, null, normal, tangent, binormal); - VertexBuffer tpNB = this.getMesh().getBuffer(Type.Normal); - VertexBuffer leftNB = left.getMesh().getBuffer(Type.Normal); - VertexBuffer downNB = bottom.getMesh().getBuffer(Type.Normal); - - BufferUtils.setInBuffer(normal, (FloatBuffer)tpNB.getData(), (s+1)*(s)); - BufferUtils.setInBuffer(normal, (FloatBuffer)leftNB.getData(), (s+1)*(i+1)-1); - BufferUtils.setInBuffer(normal, (FloatBuffer)downNB.getData(), 0); + averageNormalsTangents(topPoint, rootPoint, leftPoint, bottomPoint, rightPoint, normal, tangent, binormal); + setInBuffer(this.getMesh(), (s+1)*(s), normal, tangent, binormal); + setInBuffer(left.getMesh(), (s+1)*(s+1)-1, normal, tangent, binormal); + setInBuffer(bottom.getMesh(), 0, normal, tangent, binormal); if (bottomLeft != null) { - VertexBuffer bottomLeftNB = bottomLeft.getMesh().getBuffer(Type.Normal); - BufferUtils.setInBuffer(normal, (FloatBuffer)bottomLeftNB.getData(), s); - bottomLeftNB.setUpdateNeeded(); + // setInBuffer(bottomLeft.getMesh(), s, normal, tangent, binormal); } - downNB.setUpdateNeeded(); } } else { // all in the middle - topPoint.set(0, this.getHeightmapHeight(0,i-1), i-1); - bottomPoint.set(0, this.getHeightmapHeight(0,i+1), i+1); + topPoint.set(0, this.getHeightmapHeight(0,i-1), -1); + bottomPoint.set(0, this.getHeightmapHeight(0,i+1), 1); - averageNormalsTangents(topPoint, rootPoint, leftPoint, bottomPoint, rightPoint, null, null, null, null, null, normal, tangent, binormal); - VertexBuffer tpNB = this.getMesh().getBuffer(Type.Normal); - VertexBuffer leftNB = left.getMesh().getBuffer(Type.Normal); - BufferUtils.setInBuffer(normal, (FloatBuffer)tpNB.getData(), (s+1)*(i)); - BufferUtils.setInBuffer(normal, (FloatBuffer)leftNB.getData(), (s+1)*(i+1)-1); + averageNormalsTangents(topPoint, rootPoint, leftPoint, bottomPoint, rightPoint, normal, tangent, binormal); + setInBuffer(this.getMesh(), (s+1)*(i), normal, tangent, binormal); + setInBuffer(left.getMesh(), (s+1)*(i+1)-1, normal, tangent, binormal); } } - left.getMesh().getBuffer(Type.Normal).setUpdateNeeded(); } - + if (top != null) { // top side, works its way right for (int i=0; i<s+1; i++) { - rootPoint.set(i, this.getHeightmapHeight(i,0), 0); - topPoint.set(i, top.getHeightmapHeight(i,s-1), -1); - bottomPoint.set(i, this.getHeightmapHeight(i,1), 1); + rootPoint.set(0, this.getHeightmapHeight(i,0), 0); + topPoint.set(0, top.getHeightmapHeight(i,s-1), -1); + bottomPoint.set(0, this.getHeightmapHeight(i,1), 1); if (i == 0) { // left corner // handled by left side pass @@ -518,49 +494,39 @@ // handled by this patch when it does its right side } else { // all in the middle - leftPoint.set(i-1, this.getHeightmapHeight(i-1,0), 0); - rightPoint.set(i+1, this.getHeightmapHeight(i+1,0), 0); - averageNormalsTangents(topPoint, rootPoint, leftPoint, bottomPoint, rightPoint, null, null, null, null, null, normal, tangent, binormal); - VertexBuffer tpNB = this.getMesh().getBuffer(Type.Normal); - BufferUtils.setInBuffer(normal, (FloatBuffer)tpNB.getData(), i); - VertexBuffer topNB = top.getMesh().getBuffer(Type.Normal); - BufferUtils.setInBuffer(normal, (FloatBuffer)topNB.getData(), (s+1)*(s)+i); + leftPoint.set(-1, this.getHeightmapHeight(i-1,0), 0); + rightPoint.set(1, this.getHeightmapHeight(i+1,0), 0); + averageNormalsTangents(topPoint, rootPoint, leftPoint, bottomPoint, rightPoint, normal, tangent, binormal); + setInBuffer(this.getMesh(), i, normal, tangent, binormal); + setInBuffer(top.getMesh(), (s+1)*(s)+i, normal, tangent, binormal); } } - top.getMesh().getBuffer(Type.Normal).setUpdateNeeded(); } if (bottom != null) { // bottom side, works its way right for (int i=0; i<s+1; i++) { - rootPoint.set(i, this.getHeightmapHeight(i,s), s); - topPoint.set(i, this.getHeightmapHeight(i,s-1), s-1); - bottomPoint.set(i, bottom.getHeightmapHeight(i,1), s+1); + rootPoint.set(0, this.getHeightmapHeight(i,s), 0); + topPoint.set(0, this.getHeightmapHeight(i,s-1), -1); + bottomPoint.set(0, bottom.getHeightmapHeight(i,1), 1); if (i == 0) { // left // handled by the left side pass } else if (i == s) { // right - // handled by this patch when it does its right side + // handled by the right side pass } else { // all in the middle - leftPoint.set(i-1, this.getHeightmapHeight(i-1,s), s); - rightPoint.set(i+1, this.getHeightmapHeight(i+1,s), s); - averageNormalsTangents(topPoint, rootPoint, leftPoint, bottomPoint, rightPoint, null, null, null, null, null, normal, tangent, binormal); - VertexBuffer tpNB = this.getMesh().getBuffer(Type.Normal); - BufferUtils.setInBuffer(normal, (FloatBuffer)tpNB.getData(), (s+1)*(s)+i); - VertexBuffer downNB = bottom.getMesh().getBuffer(Type.Normal); - BufferUtils.setInBuffer(normal, (FloatBuffer)downNB.getData(), i); + leftPoint.set(-1, this.getHeightmapHeight(i-1,s), 0); + rightPoint.set(1, this.getHeightmapHeight(i+1,s), 0); + averageNormalsTangents(topPoint, rootPoint, leftPoint, bottomPoint, rightPoint, normal, tangent, binormal); + setInBuffer(this.getMesh(), (s+1)*(s)+i, normal, tangent, binormal); + setInBuffer(bottom.getMesh(), i, normal, tangent, binormal); } } - bottom.getMesh().getBuffer(Type.Normal).setUpdateNeeded(); } - - this.getMesh().getBuffer(Type.Normal).setUpdateNeeded(); - this.getMesh().getBuffer(Type.Tangent).setUpdateNeeded(); - this.getMesh().getBuffer(Type.Binormal).setUpdateNeeded(); } protected void averageNormalsTangents( @@ -569,38 +535,36 @@ Vector3f leftPoint, Vector3f bottomPoint, Vector3f rightPoint, - Vector2f topTex, - Vector2f rootTex, - Vector2f leftTex, - Vector2f bottomTex, - Vector2f rightTex, Vector3f normal, Vector3f tangent, Vector3f binormal) { Vector3f scale = getWorldScale(); - Vector3f n1 = Vector3f.ZERO; + Vector3f n1 = new Vector3f(0,0,0); if (topPoint != null && leftPoint != null) { - n1 = calculateNormal(topPoint.mult(scale), rootPoint.mult(scale), leftPoint.mult(scale)); + n1.set(calculateNormal(topPoint.mult(scale), rootPoint.mult(scale), leftPoint.mult(scale))); } - Vector3f n2 = Vector3f.ZERO; + Vector3f n2 = new Vector3f(0,0,0); if (leftPoint != null && bottomPoint != null) { - n2 = calculateNormal(leftPoint.mult(scale), rootPoint.mult(scale), bottomPoint.mult(scale)); + n2.set(calculateNormal(leftPoint.mult(scale), rootPoint.mult(scale), bottomPoint.mult(scale))); } - Vector3f n3 = Vector3f.ZERO; + Vector3f n3 = new Vector3f(0,0,0); if (rightPoint != null && bottomPoint != null) { - n3 = calculateNormal(bottomPoint.mult(scale), rootPoint.mult(scale), rightPoint.mult(scale)); + n3.set(calculateNormal(bottomPoint.mult(scale), rootPoint.mult(scale), rightPoint.mult(scale))); } - Vector3f n4 = Vector3f.ZERO; + Vector3f n4 = new Vector3f(0,0,0); if (rightPoint != null && topPoint != null) { - n4 = calculateNormal(rightPoint.mult(scale), rootPoint.mult(scale), topPoint.mult(scale)); + n4.set(calculateNormal(rightPoint.mult(scale), rootPoint.mult(scale), topPoint.mult(scale))); } - if (bottomPoint != null && rightPoint != null && rootTex != null && rightTex != null && bottomTex != null) - LODGeomap.calculateTangent(new Vector3f[]{rootPoint.mult(scale),rightPoint.mult(scale),bottomPoint.mult(scale)}, new Vector2f[]{rootTex,rightTex,bottomTex}, tangent, binormal); + //if (bottomPoint != null && rightPoint != null && rootTex != null && rightTex != null && bottomTex != null) + // LODGeomap.calculateTangent(new Vector3f[]{rootPoint.mult(scale),rightPoint.mult(scale),bottomPoint.mult(scale)}, new Vector2f[]{rootTex,rightTex,bottomTex}, tangent, binormal); - normal.set(n1.add(n2).add(n3).add(n4).normalizeLocal()); + normal.set(n1.add(n2).add(n3).add(n4).normalize()); + + tangent.set(normal.cross(new Vector3f(0,0,1)).normalize()); + binormal.set(new Vector3f(1,0,0).cross(normal).normalize()); } private Vector3f calculateNormal(Vector3f firstPoint, Vector3f rootPoint, Vector3f secondPoint) { diff --git a/engine/src/terrain/com/jme3/terrain/geomipmap/TerrainQuad.java b/engine/src/terrain/com/jme3/terrain/geomipmap/TerrainQuad.java index 0b9b218..1086c71 100644 --- a/engine/src/terrain/com/jme3/terrain/geomipmap/TerrainQuad.java +++ b/engine/src/terrain/com/jme3/terrain/geomipmap/TerrainQuad.java
@@ -1675,8 +1675,9 @@ } } } - else + else if (children.get(i) instanceof TerrainQuad) { ((TerrainQuad) children.get(i)).findPick(toTest, results); + } } } } diff --git a/engine/src/test/jme3test/app/TestAppStateLifeCycle.java b/engine/src/test/jme3test/app/TestAppStateLifeCycle.java new file mode 100644 index 0000000..f224242 --- /dev/null +++ b/engine/src/test/jme3test/app/TestAppStateLifeCycle.java
@@ -0,0 +1,126 @@ +/* + * Copyright (c) 2009-2012 jMonkeyEngine + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are + * met: + * + * * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * + * * Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * * Neither the name of 'jMonkeyEngine' nor the names of its contributors + * may be used to endorse or promote products derived from this software + * without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED + * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR + * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR + * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, + * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, + * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR + * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF + * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING + * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +package jme3test.app; + +import com.jme3.app.Application; +import com.jme3.app.SimpleApplication; +import com.jme3.app.state.AbstractAppState; +import com.jme3.app.state.AppStateManager; +import com.jme3.material.Material; +import com.jme3.math.Vector3f; +import com.jme3.renderer.RenderManager; +import com.jme3.scene.Geometry; +import com.jme3.scene.shape.Box; + + +/** + * Tests the app state lifecycles. + * + * @author Paul Speed + */ +public class TestAppStateLifeCycle extends SimpleApplication { + + public static void main(String[] args){ + TestAppStateLifeCycle app = new TestAppStateLifeCycle(); + app.start(); + } + + @Override + public void simpleInitApp() { + Box b = new Box(Vector3f.ZERO, 1, 1, 1); + Geometry geom = new Geometry("Box", b); + Material mat = new Material(assetManager, "Common/MatDefs/Misc/Unshaded.j3md"); + mat.setTexture("ColorMap", assetManager.loadTexture("Interface/Logo/Monkey.jpg")); + geom.setMaterial(mat); + rootNode.attachChild(geom); + + System.out.println("Attaching test state."); + stateManager.attach(new TestState()); + } + + @Override + public void simpleUpdate(float tpf) { + + if(stateManager.getState(TestState.class) != null) { + System.out.println("Detaching test state."); + stateManager.detach(stateManager.getState(TestState.class)); + System.out.println("Done"); + } + } + + public class TestState extends AbstractAppState { + + @Override + public void initialize(AppStateManager stateManager, Application app) { + super.initialize(stateManager, app); + System.out.println("Initialized"); + } + + @Override + public void stateAttached(AppStateManager stateManager) { + super.stateAttached(stateManager); + System.out.println("Attached"); + } + + @Override + public void update(float tpf) { + super.update(tpf); + System.out.println("update"); + } + + @Override + public void render(RenderManager rm) { + super.render(rm); + System.out.println("render"); + } + + @Override + public void postRender() { + super.postRender(); + System.out.println("postRender"); + } + + @Override + public void stateDetached(AppStateManager stateManager) { + super.stateDetached(stateManager); + System.out.println("Detached"); + } + + @Override + public void cleanup() { + super.cleanup(); + System.out.println("Cleanup"); + } + + } +} diff --git a/engine/src/test/jme3test/effect/TestSoftParticles.java b/engine/src/test/jme3test/effect/TestSoftParticles.java new file mode 100644 index 0000000..d779a6f --- /dev/null +++ b/engine/src/test/jme3test/effect/TestSoftParticles.java
@@ -0,0 +1,130 @@ +/* + * To change this template, choose Tools | Templates + * and open the template in the editor. + */ +package jme3test.effect; + +import com.jme3.app.SimpleApplication; +import com.jme3.effect.ParticleEmitter; +import com.jme3.effect.ParticleMesh; +import com.jme3.effect.shapes.EmitterSphereShape; +import com.jme3.input.KeyInput; +import com.jme3.input.controls.ActionListener; +import com.jme3.input.controls.KeyTrigger; +import com.jme3.material.Material; +import com.jme3.math.ColorRGBA; +import com.jme3.math.Quaternion; +import com.jme3.math.Vector3f; +import com.jme3.post.FilterPostProcessor; +import com.jme3.post.filters.TranslucentBucketFilter; +import com.jme3.scene.Geometry; +import com.jme3.scene.shape.Box; + +/** + * + * @author Nehon + */ +public class TestSoftParticles extends SimpleApplication { + + private boolean softParticles = true; + private FilterPostProcessor fpp; + private TranslucentBucketFilter tbf; + + public static void main(String[] args) { + TestSoftParticles app = new TestSoftParticles(); + app.start(); + } + + @Override + public void simpleInitApp() { + + cam.setLocation(new Vector3f(-7.2221026f, 4.1183004f, 7.759811f)); + cam.setRotation(new Quaternion(0.06152846f, 0.91236454f, -0.1492115f, 0.37621948f)); + + flyCam.setMoveSpeed(10); + + + // -------- floor + Box b = new Box(Vector3f.ZERO, 10, 0.1f, 10); + Geometry geom = new Geometry("Box", b); + Material mat = new Material(assetManager, "Common/MatDefs/Misc/Unshaded.j3md"); + mat.setColor("Color", ColorRGBA.Gray); + mat.setTexture("ColorMap", assetManager.loadTexture("Interface/Logo/Monkey.jpg")); + geom.setMaterial(mat); + rootNode.attachChild(geom); + + Box b2 = new Box(Vector3f.ZERO, 1, 1, 1); + Geometry geom2 = new Geometry("Box", b2); + Material mat2 = new Material(assetManager, "Common/MatDefs/Misc/Unshaded.j3md"); + mat2.setColor("Color", ColorRGBA.DarkGray); + geom2.setMaterial(mat2); + rootNode.attachChild(geom2); + geom2.setLocalScale(0.1f, 0.2f, 1); + + fpp = new FilterPostProcessor(assetManager); + tbf = new TranslucentBucketFilter(true); + fpp.addFilter(tbf); + viewPort.addProcessor(fpp); + + + Material material = new Material(assetManager, "Common/MatDefs/Misc/Particle.j3md"); + material.setTexture("Texture", assetManager.loadTexture("Effects/Explosion/flame.png")); + + material.setFloat("Softness", 3f); // + + + //Fire + ParticleEmitter fire = new ParticleEmitter("Fire", ParticleMesh.Type.Triangle, 30); + fire.setMaterial(material); + fire.setShape(new EmitterSphereShape(Vector3f.ZERO, 0.1f)); + fire.setImagesX(2); + fire.setImagesY(2); // 2x2 texture animation + fire.setEndColor(new ColorRGBA(1f, 0f, 0f, 1f)); // red + fire.setStartColor(new ColorRGBA(1f, 1f, 0f, 0.5f)); // yellow + fire.setStartSize(0.6f); + fire.setEndSize(0.01f); + fire.setGravity(0, -0.3f, 0); + fire.setLowLife(0.5f); + fire.setHighLife(3f); + fire.setLocalTranslation(0, 0.2f, 0); + + rootNode.attachChild(fire); + + + ParticleEmitter smoke = new ParticleEmitter("Smoke", ParticleMesh.Type.Triangle, 30); + smoke.setMaterial(material); + smoke.setShape(new EmitterSphereShape(Vector3f.ZERO, 5)); + smoke.setImagesX(1); + smoke.setImagesY(1); // 2x2 texture animation + smoke.setStartColor(new ColorRGBA(0.1f, 0.1f, 0.1f,1f)); // dark gray + smoke.setEndColor(new ColorRGBA(0.5f, 0.5f, 0.5f, 0.3f)); // gray + smoke.setStartSize(3f); + smoke.setEndSize(5f); + smoke.setGravity(0, -0.001f, 0); + smoke.setLowLife(100f); + smoke.setHighLife(100f); + smoke.setLocalTranslation(0, 0.1f, 0); + smoke.emitAllParticles(); + + rootNode.attachChild(smoke); + + + inputManager.addListener(new ActionListener() { + + public void onAction(String name, boolean isPressed, float tpf) { + if(isPressed && name.equals("toggle")){ + // tbf.setEnabled(!tbf.isEnabled()); + softParticles = !softParticles; + if(softParticles){ + viewPort.addProcessor(fpp); + }else{ + viewPort.removeProcessor(fpp); + } + } + } + }, "toggle"); + inputManager.addMapping("toggle", new KeyTrigger(KeyInput.KEY_SPACE)); + } + + +} diff --git a/engine/src/test/jme3test/helloworld/HelloMaterial.java b/engine/src/test/jme3test/helloworld/HelloMaterial.java index e930c91..8ef6f56 100644 --- a/engine/src/test/jme3test/helloworld/HelloMaterial.java +++ b/engine/src/test/jme3test/helloworld/HelloMaterial.java
@@ -80,7 +80,7 @@ /** A cube with its base color "leaking" through a partially transparent texture */ Box boxshape4 = new Box(new Vector3f(3f,-1f,0f), 1f,1f,1f); Geometry cube_leak = new Geometry("Leak-through color cube", boxshape4); - Material mat_tl = new Material(assetManager, "Common/MatDefs/Misc/ColoredTextured.j3md"); + Material mat_tl = new Material(assetManager, "Common/MatDefs/Misc/Unshaded.j3md"); mat_tl.setTexture("ColorMap", assetManager.loadTexture("Textures/ColoredTex/Monkey.png")); mat_tl.setColor("Color", new ColorRGBA(1f,0f,1f, 1f)); // purple cube_leak.setMaterial(mat_tl); @@ -103,6 +103,7 @@ shiny_rock.setLocalTranslation(0,2,-2); // Move it a bit shiny_rock.rotate(1.6f, 0, 0); // Rotate it a bit rootNode.attachChild(shiny_rock); + /** Must add a light to make the lit object visible! */ DirectionalLight sun = new DirectionalLight(); sun.setDirection(new Vector3f(1,0,-2).normalizeLocal()); diff --git a/engine/src/test/jme3test/model/shape/TestExpandingTorus.java b/engine/src/test/jme3test/model/shape/TestExpandingTorus.java new file mode 100644 index 0000000..9e76b3f --- /dev/null +++ b/engine/src/test/jme3test/model/shape/TestExpandingTorus.java
@@ -0,0 +1,45 @@ +/* + * To change this template, choose Tools | Templates + * and open the template in the editor. + */ +package jme3test.model.shape; + +import com.jme3.app.SimpleApplication; +import com.jme3.material.Material; +import com.jme3.scene.Geometry; +import com.jme3.scene.shape.Torus; + +public class TestExpandingTorus extends SimpleApplication { + + private float outerRadius = 1.5f; + private float rate = 1; + private Torus torus; + private Geometry geom; + + public static void main(String[] args) { + TestExpandingTorus app = new TestExpandingTorus(); + app.start(); + } + + @Override + public void simpleInitApp() { + torus = new Torus(30, 10, .5f, 1f); + geom = new Geometry("Torus", torus); + Material mat = new Material(assetManager, "Common/MatDefs/Misc/Unshaded.j3md"); + geom.setMaterial(mat); + rootNode.attachChild(geom); + } + + @Override + public void simpleUpdate(float tpf){ + if (outerRadius > 2.5f){ + outerRadius = 2.5f; + rate = -rate; + }else if (outerRadius < 1f){ + outerRadius = 1f; + rate = -rate; + } + outerRadius += rate * tpf; + torus.updateGeometry(30, 10, .5f, outerRadius); + } +} \ No newline at end of file diff --git a/engine/src/tools/jme3tools/converters/model/ModelConverter.java b/engine/src/tools/jme3tools/converters/model/ModelConverter.java index 2539574..6383d43 100644 --- a/engine/src/tools/jme3tools/converters/model/ModelConverter.java +++ b/engine/src/tools/jme3tools/converters/model/ModelConverter.java
@@ -69,7 +69,7 @@ ts.setListsOnly(listOnly); ts.setMinStripSize(minStripSize); - IndexBuffer ib = mesh.getIndexBuffer(); + IndexBuffer ib = mesh.getIndicesAsList(); int[] indices = new int[ib.size()]; for (int i = 0; i < indices.length; i++) indices[i] = ib.get(i); @@ -157,7 +157,7 @@ mesh.setBuffer(vb); } } - mesh.setInterleaved(); + //mesh.setInterleaved(); } private static void optimizeScene(Spatial source, boolean toFixed){ diff --git a/engine/src/tools/jme3tools/optimize/GeometryBatchFactory.java b/engine/src/tools/jme3tools/optimize/GeometryBatchFactory.java index ae6ad8c..121d75c 100644 --- a/engine/src/tools/jme3tools/optimize/GeometryBatchFactory.java +++ b/engine/src/tools/jme3tools/optimize/GeometryBatchFactory.java
@@ -62,26 +62,27 @@ } } - private static void doTransformTangents(FloatBuffer inBuf, int offset, FloatBuffer outBuf, Matrix4f transform) { + private static void doTransformTangents(FloatBuffer inBuf, int offset, int components, FloatBuffer outBuf, Matrix4f transform) { Vector3f tan = new Vector3f(); - float handedness = 0; + // offset is given in element units // convert to be in component units - offset *= 4; + offset *= components; - for (int i = 0; i < inBuf.capacity() / 4; i++) { - tan.x = inBuf.get(i * 4 + 0); - tan.y = inBuf.get(i * 4 + 1); - tan.z = inBuf.get(i * 4 + 2); - handedness = inBuf.get(i * 4 + 3); + for (int i = 0; i < inBuf.capacity() / components; i++) { + tan.x = inBuf.get(i * components + 0); + tan.y = inBuf.get(i * components + 1); + tan.z = inBuf.get(i * components + 2); transform.multNormal(tan, tan); - outBuf.put(offset + i * 4 + 0, tan.x); - outBuf.put(offset + i * 4 + 1, tan.y); - outBuf.put(offset + i * 4 + 2, tan.z); - outBuf.put(offset + i * 4 + 3, handedness); - + outBuf.put(offset + i * components + 0, tan.x); + outBuf.put(offset + i * components + 1, tan.y); + outBuf.put(offset + i * components + 2, tan.z); + + if (components == 4){ + outBuf.put(offset + i * components + 3, inBuf.get(i * components + 3)); + } } } @@ -129,9 +130,9 @@ throw new UnsupportedOperationException(); } - for (Entry<VertexBuffer> entry : geom.getMesh().getBuffers()) { - compsForBuf[entry.getKey()] = entry.getValue().getNumComponents(); - formatForBuf[entry.getKey()] = entry.getValue().getFormat(); + for (VertexBuffer vb : geom.getMesh().getBufferList().getArray()){ + compsForBuf[vb.getBufferType().ordinal()] = vb.getNumComponents(); + formatForBuf[vb.getBufferType().ordinal()] = vb.getFormat(); } if (mode != null && mode != listMode) { @@ -208,10 +209,11 @@ FloatBuffer inPos = (FloatBuffer) inBuf.getDataReadOnly(); FloatBuffer outPos = (FloatBuffer) outBuf.getData(); doTransformNorms(inPos, globalVertIndex, outPos, worldMatrix); - }else if(Type.Tangent.ordinal() == bufType){ + }else if(Type.Tangent.ordinal() == bufType){ FloatBuffer inPos = (FloatBuffer) inBuf.getDataReadOnly(); FloatBuffer outPos = (FloatBuffer) outBuf.getData(); - doTransformTangents(inPos, globalVertIndex, outPos, worldMatrix); + int components = inBuf.getNumComponents(); + doTransformTangents(inPos, globalVertIndex, components, outPos, worldMatrix); } else { inBuf.copyElements(0, outBuf, globalVertIndex, geomVertCount); }